diff --git a/.changeset/nice-sails-trade.md b/.changeset/nice-sails-trade.md new file mode 100644 index 00000000000..8d38da4e1a1 --- /dev/null +++ b/.changeset/nice-sails-trade.md @@ -0,0 +1,28 @@ +--- +'@forgerock/davinci-client': minor +'@forgerock/journey-client': minor +'@forgerock/oidc-client': minor +'@forgerock/sdk-store': minor +'@forgerock/sdk-oidc': major +--- + +Allow multiple SDK clients to share a single Redux store. + +`davinci()`, `journey()`, and `oidc()` now accept an optional `store` option. When two clients share a store they share the OpenID Connect discovery cache, so `.well-known/openid-configuration` is fetched once instead of once per client. `davinci()` and `journey()` expose the store they create as `client.store`; applications that want to own the store themselves can build one with `createSdkStore()` from the new `@forgerock/sdk-store` package. + +Omitting `store` is unchanged behaviour: the client creates its own store, exactly as before. + +**Request middleware and logging are scoped per client.** Each client's `requestMiddleware` and `logger` are registered against that client alone and are resolved only by its own requests. Middleware passed to `davinci()` or `journey()` is never applied to OIDC requests (`AUTHORIZE`, `PAR`, `TOKEN_EXCHANGE`, `REVOKE`, `USER_INFO`, `END_SESSION`), and middleware passed to `oidc()` is never applied to DaVinci or Journey requests. Both options are honoured on a shared store. + +**`oidc()` takes `store` as part of its options object**, alongside `config`, `requestMiddleware`, `logger`, and `storage`, consistent with every other factory in the SDK. + +**One OIDC client per store.** `oidc()` mounts at a fixed key, so initialising a second OIDC client on the same store with a different `clientId` returns an `argument_error` rather than silently overwriting the first client's token state. Re-initialising with the same `clientId` is allowed and idempotent. Use a separate store per `clientId`. + +Also in this release: + +- New `@forgerock/sdk-store` package (`scope:sdk-effects`) holding the single canonical `wellknownApi` instance, the shared store contract (`SdkStore`, `SdkStoreHandle`, `createSdkStore`, `injectClient`), and OpenID Connect discovery helpers (`initWellknownQuery`, `isValidWellknownResponse`). Previously each client package defined its own `wellknownApi`, which meant a separate discovery cache per client. +- `oidc()` validates its arguments before attaching to a store, so a rejected call no longer leaves a caller-provided store modified. +- Passing a value that is not an SDK store to `store` returns an `argument_error` instead of throwing. +- Well-known selectors are now memoized per URL. `createWellknownSelector` previously rebuilt its selector on every call, so its cache never took effect. +- `@forgerock/sdk-oidc`: `initWellknownQuery` and `isValidWellknownResponse` move to `@forgerock/sdk-store`. Update imports if you were using them directly. +- `enforce-module-boundaries` lint rule promoted from `warn` to `error` across the repo. All packages pass. diff --git a/e2e/davinci-app/main.ts b/e2e/davinci-app/main.ts index e42ee2030a6..59f2f6d4133 100644 --- a/e2e/davinci-app/main.ts +++ b/e2e/davinci-app/main.ts @@ -30,7 +30,6 @@ import { serverConfigs } from './server-configs.js'; import type { Collectors, CustomLogger, - DavinciClient, DaVinciConfig, GetClient, InternalErrorResponse, @@ -88,7 +87,11 @@ const requestMiddleware: RequestMiddleware<'DAVINCI_NEXT' | 'DAVINCI_START'>[] = const urlParams = new URLSearchParams(window.location.search); (async () => { - const davinciClient: DavinciClient = await davinci({ config, logger, requestMiddleware }); + const davinciResult = await davinci({ config, logger, requestMiddleware }); + if ('error' in davinciResult) { + throw new Error(`Failed to initialize davinci client: ${davinciResult.error}`); + } + const davinciClient = davinciResult; const oidcResult = await oidc({ config: config as OidcConfig }); if ('error' in oidcResult) { throw new Error(`Failed to initialize oidc client: ${oidcResult.error}`); diff --git a/e2e/davinci-app/shared-store.html b/e2e/davinci-app/shared-store.html new file mode 100644 index 00000000000..e0551b54f43 --- /dev/null +++ b/e2e/davinci-app/shared-store.html @@ -0,0 +1,12 @@ + + + + + + Shared Store Test + + +
initialising…
+ + + diff --git a/e2e/davinci-app/shared-store.ts b/e2e/davinci-app/shared-store.ts new file mode 100644 index 00000000000..8820b1f39a4 --- /dev/null +++ b/e2e/davinci-app/shared-store.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +/** + * Shared-store smoke test entry point. + * + * This page is navigated to by the Playwright e2e suite + * `shared-store.test.ts` only. It does not connect to any real PingOne + * endpoint — the test intercepts every `.well-known` request via + * `page.route()` and returns a minimal synthetic response. + * + * The page reports results by writing to `#status` so the test can + * assert via `page.textContent` without any app-specific UI. + */ +import { davinci } from '@forgerock/davinci-client'; +import { oidc } from '@forgerock/oidc-client'; + +import type { DaVinciConfig } from '@forgerock/davinci-client/types'; +import type { OidcConfig } from '@forgerock/oidc-client/types'; + +const WELLKNOWN_URL = 'https://sdk-test.example.com/as/.well-known/openid-configuration'; + +const davinciConfig: DaVinciConfig = { + clientId: 'test-davinci-client', + redirectUri: window.location.origin, + scope: 'openid profile', + serverConfig: { wellknown: WELLKNOWN_URL }, +}; + +const oidcConfig: OidcConfig = { + clientId: 'test-oidc-client', + redirectUri: window.location.origin, + scope: 'openid profile', + responseType: 'code', + serverConfig: { wellknown: WELLKNOWN_URL }, +}; + +const statusEl = document.getElementById('status')!; + +async function run() { + // ── Mode 2: davinci creates the store, oidc attaches ───────────────────── + const dvClient = await davinci({ config: davinciConfig }); + if ('error' in dvClient) { + statusEl.textContent = `davinci init error: ${dvClient.error}`; + return; + } + + const ocClient = await oidc({ config: oidcConfig, store: dvClient.store }); + if ('error' in ocClient) { + statusEl.textContent = `oidc init error: ${ocClient.error}`; + return; + } + + statusEl.textContent = 'ready'; +} + +run().catch((err) => { + statusEl.textContent = `unexpected error: ${String(err)}`; +}); diff --git a/e2e/davinci-app/vite.config.ts b/e2e/davinci-app/vite.config.ts index 8625a27297c..b885b3911c8 100644 --- a/e2e/davinci-app/vite.config.ts +++ b/e2e/davinci-app/vite.config.ts @@ -17,6 +17,7 @@ export default defineConfig({ rollupOptions: { input: { main: path.resolve(__dirname, 'index.html'), + 'shared-store': path.resolve(__dirname, 'shared-store.html'), }, output: { entryFileNames: 'main.js', diff --git a/e2e/davinci-suites/src/shared-store.test.ts b/e2e/davinci-suites/src/shared-store.test.ts new file mode 100644 index 00000000000..7fa4f96c127 --- /dev/null +++ b/e2e/davinci-suites/src/shared-store.test.ts @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { expect, test } from '@playwright/test'; + +/** + * Verifies that two SDK clients sharing a store fetch the OpenID Connect + * discovery document exactly once, regardless of which client initialises + * first and regardless of the ownership model. + * + * The page under test (`/shared-store`) exercises both Mode 2 (davinci owns + * the store) and Mode 3 (consumer-created store). Requests to the well-known + * URL are intercepted and served with a synthetic response so the test does + * not require a live PingOne endpoint. + */ + +const WELLKNOWN_URL = 'https://sdk-test.example.com/as/.well-known/openid-configuration'; + +const WELLKNOWN_RESPONSE = { + issuer: 'https://sdk-test.example.com/as', + authorization_endpoint: 'https://sdk-test.example.com/as/authorize', + token_endpoint: 'https://sdk-test.example.com/as/token', + userinfo_endpoint: 'https://sdk-test.example.com/as/userinfo', + jwks_uri: 'https://sdk-test.example.com/as/jwks', + revocation_endpoint: 'https://sdk-test.example.com/as/revoke', + introspection_endpoint: 'https://sdk-test.example.com/as/introspect', + pushed_authorization_request_endpoint: 'https://sdk-test.example.com/as/par', +}; + +test('shared store — one .well-known fetch across two clients (mode 2: client-owned)', async ({ + page, +}) => { + let discoveryFetchCount = 0; + + // Intercept and count every discovery request; fulfil with a synthetic response + // so no live credential or network is needed. + await page.route(`**/.well-known/**`, async (route) => { + discoveryFetchCount++; + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(WELLKNOWN_RESPONSE), + }); + }); + + await page.goto('/shared-store.html', { waitUntil: 'networkidle' }); + + // The page reports its own status so we know initialisation completed. + await expect(page.locator('#status')).toHaveText('ready', { timeout: 15_000 }); + + // Mode 2 (davinci owns the store, oidc attaches): davinci fetches once, + // oidc reads from cache — exactly 1 network request for 2 clients. + expect(discoveryFetchCount).toBe(1); +}); + +test("shared store — oidc attaches to davinci's store, reads discovery from cache", async ({ + page, +}) => { + const fetchedUrls: string[] = []; + + await page.route(`**/.well-known/**`, async (route) => { + fetchedUrls.push(route.request().url()); + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify(WELLKNOWN_RESPONSE), + }); + }); + + await page.goto('/shared-store.html', { waitUntil: 'networkidle' }); + await expect(page.locator('#status')).toHaveText('ready', { timeout: 15_000 }); + + // Both modes use the same WELLKNOWN_URL, so each URL appears exactly once + // across the two calls despite four total client initialisations. + const unique = [...new Set(fetchedUrls)]; + expect(unique).toHaveLength(1); + expect(unique[0]).toBe(WELLKNOWN_URL); + + // Mode 2: 2 clients (davinci + oidc) on 1 store → exactly 1 fetch. + expect(fetchedUrls.length).toBe(1); +}); diff --git a/e2e/device-client-app/src/utils/index.ts b/e2e/device-client-app/src/utils/index.ts index 721d453d025..598512560c0 100644 --- a/e2e/device-client-app/src/utils/index.ts +++ b/e2e/device-client-app/src/utils/index.ts @@ -20,7 +20,12 @@ import type { JourneyResult, JourneyStep, } from '@forgerock/journey-client/types'; -import type { OidcClient, OidcConfig, UserInfoResponse } from '@forgerock/oidc-client/types'; +import type { + GenericError, + OidcClient, + OidcConfig, + UserInfoResponse, +} from '@forgerock/oidc-client/types'; let cachedOidcClient: OidcClient | null = null; @@ -113,14 +118,19 @@ export const LoginAndGetClient = Effect.gen(function* () { catch: (err) => new Error(`Failed to initialize OIDC client: ${err}`), }); + if ('error' in journeyClient) { + throw new Error(`Failed to initialize journey client: ${journeyClient.error}`); + } + if ('error' in oidcClient) { - return yield* Effect.fail(new Error(`Failed to initialize OIDC client: ${oidcClient.error}`)); + throw new Error(`Failed to initialize OIDC client: ${oidcClient.error}`); } cachedOidcClient = oidcClient; yield* Effect.tryPromise({ - try: () => oidcClientOrThrow().user.logout(), + try: () => + (cachedOidcClient as Exclude).user.logout(), catch: (err) => new Error(`Logout failed: ${err}`), }).pipe(Effect.catchAll((err) => Console.warn('Logout failed, continuing:', err))); @@ -144,7 +154,13 @@ export const LoginAndGetClient = Effect.gen(function* () { Effect.flatMap((step) => checkForLoginSuccess(step)), Effect.flatMap(() => Effect.tryPromise({ - try: () => oidcClientOrThrow().token.get({ backgroundRenew: true }), + try: () => { + const client = oidcClientOrThrow(); + if ('error' in client) { + throw new Error(`OIDC client is in error state: ${client.error}`); + } + return client.token.get({ backgroundRenew: true }); + }, catch: (err) => new Error(`Failed to get tokens: ${err}`), }).pipe(Effect.tap((tokens) => Console.log('Got Tokens', tokens))), ), @@ -156,7 +172,11 @@ export const LoginAndGetClient = Effect.gen(function* () { export const getUser = Effect.tryPromise({ try: async (): Promise => { - const response = await oidcClientOrThrow().user.info(); + const client = oidcClientOrThrow(); + if ('error' in client) { + throw new Error(`OIDC client is in error state: ${client.error}`); + } + const response = await client.user.info(); if ('error' in response) { throw new Error(`Failed to get user info: ${response.error}`); } diff --git a/e2e/journey-app/main.ts b/e2e/journey-app/main.ts index e5c0d735dbb..80844d3fd04 100644 --- a/e2e/journey-app/main.ts +++ b/e2e/journey-app/main.ts @@ -15,7 +15,7 @@ import { handleWebAuthnStep } from './components/webauthn-step.js'; import { serverConfigs } from './server-configs.js'; import { deleteWebAuthnDevice } from './services/delete-webauthn-device.js'; -import type { JourneyClient, RequestMiddleware } from '@forgerock/journey-client/types'; +import type { RequestMiddleware } from '@forgerock/journey-client/types'; const qs = window.location.search; const searchParams = new URLSearchParams(qs); @@ -61,15 +61,14 @@ if (searchParams.get('middleware') === 'true') { const formEl = document.getElementById('form') as HTMLFormElement; const journeyEl = document.getElementById('journey') as HTMLDivElement; - let journeyClient: JourneyClient; - try { - journeyClient = await journey({ config: config, requestMiddleware }); - } catch (error) { - const message = error instanceof Error ? error.message : 'Unknown error'; + const journeyResult = await journey({ config: config, requestMiddleware }); + if ('error' in journeyResult) { + const message = journeyResult.error; console.error('Failed to initialize journey client:', message); errorEl.textContent = message; return; } + const journeyClient = journeyResult; let step = await journeyClient.start({ journey: journeyName }); function renderError() { diff --git a/eslint.config.mjs b/eslint.config.mjs index 0f5d2a1a963..6b5f0e357a4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -144,7 +144,7 @@ export default [ rules: { 'import/extensions': [2, 'ignorePackages'], '@nx/enforce-module-boundaries': [ - 'warn', + 'error', { enforceBuildableLibDependency: true, allow: [], diff --git a/packages/davinci-client/README.md b/packages/davinci-client/README.md index 5534564ffd9..a12ab1e61ee 100644 --- a/packages/davinci-client/README.md +++ b/packages/davinci-client/README.md @@ -25,7 +25,7 @@ Configure DaVinci Client with the following minimum, required properties: ```ts // Demo with example values -import { davinci } from '@forgerock/davinci'; +import { davinci } from '@forgerock/davinci-client'; const davinciClient = await davinci({ config: { @@ -42,7 +42,7 @@ If you have a need for more than one client, say you need to use two or more dif ```ts // Demo with example values -import { davinci } from '@forgerock/davinci'; +import { davinci } from '@forgerock/davinci-client'; const firstDavinciClient = await davinci(/** config 1 **/); const secondDavinciClient = await davinci(/** config 2 **/); @@ -63,6 +63,50 @@ interface DaVinciConfig { } ``` +### Sharing a store with another client + +If your application also uses `@forgerock/oidc-client`, the two can share one Redux store so the well-known discovery document is fetched once rather than once per client. + +`davinci()` exposes the store it created as `client.store`. Pass it to the other client: + +```ts +import { davinci } from '@forgerock/davinci-client'; +import { oidc } from '@forgerock/oidc-client'; + +const davinciClient = await davinci({ config }); + +// Attaches to davinci's store; the discovery document is already cached there. +const oidcClient = await oidc({ config: oidcConfig, store: davinciClient.store }); +``` + +Or create the store yourself when neither client is the natural owner: + +```ts +import { createSdkStore } from '@forgerock/sdk-store'; + +const store = createSdkStore(); +const davinciClient = await davinci({ config, store }); +const oidcClient = await oidc({ config: oidcConfig, store }); +``` + +Omitting `store` is always valid — the client creates its own, which is the default behaviour. + +#### Middleware and logging stay private + +Sharing a store shares cached data, not configuration. `requestMiddleware` and `logger` are registered against the client you pass them to, and are resolved only by that client's own requests: + +```ts +const store = createSdkStore(); + +// Runs for DAVINCI_START, DAVINCI_NEXT, DAVINCI_FLOW and the other DaVinci actions only. +await davinci({ config, store, requestMiddleware: [davinciMiddleware] }); + +// Runs for OIDC requests only. +await oidc({ config: oidcConfig, store, requestMiddleware: [oidcMiddleware] }); +``` + +Middleware passed here will never run against an OIDC token exchange, and vice versa. + ### Start a DaVinci flow Call the `start` method on the returned client API: @@ -182,10 +226,10 @@ Upon each collector in the array, some will need an `updater`, like the collecto ```ts // Example SingleValueCollector using the TextCollector -const collectors = davinci.collectors(); +const collectors = davinciClient.getCollectors(); collectors.map((collector) => { if (collector.type === 'TextCollector') { - renderTextCollector(collector, davinci.update(collector)); + renderTextCollector(collector, davinciClient.update(collector)); } }); ``` @@ -214,7 +258,7 @@ The `SubmitCollector` is associated with the submission of the current node and ```ts // Example SubmitCollector mapping -const collectors = davinci.collectors(); +const collectors = davinciClient.getCollectors(); collectors.map((collector) => { if (collector.type === 'SubmitCollector') { renderSubmitCollector( @@ -234,7 +278,7 @@ To do this, you call the `flow` method on the `davinciClient` passing the `key` ```ts // Example FlowCollector mapping -const collectors = davinci.collectors(); +const collectors = davinciClient.getCollectors(); collectors.map((collector) => { if (collector.type === 'FlowCollector') { renderFlowCollector(collector, davinciClient.flow(collector)); @@ -260,7 +304,7 @@ function renderFlowCollector(collector, startFlow) { After collecting the needed data, you proceed to the next node in the DaVinci flow by calling the `.next()` method on the same `davinci` client object. This can be the result of a user clicking on the button rendered from the `SubmitCollector`, from the "submit" event of the HTML form itself, or from programmatically triggering the submission in the application layer. ```ts -let nextStep = davinci.next(); +const nextStep = await davinciClient.next(); ``` Note: There's no need to pass anything into the `next` method as the DaVinci Client internally stores the updated object needed for the server. @@ -284,25 +328,12 @@ When you receive a success node, you will likely want to use the Authorization C Here's a brief sample of what that might look like in pseudocode: ```ts -// ... other imports - -import { Config, TokenManager } from '@forgerock/javascript-sdk'; - -// ... other config or initialization code - -// This Config.set accepts the same config schema as the davinci function -Config.set(config); - -const node = await davinciClient.next(); - -if (node.status === 'success') { - const clientInfo = davinciClient.getClient(); - - const code = clientInfo.authorization?.code || ''; - const state = clientInfo.authorization?.state || ''; - - const tokens = await TokenManager.getTokens({ query: { code, state } }); - // user now has session and OIDC tokens +// oidcClient is an instance of oidc() from @forgerock/oidc-client, configured earlier +const tokens = await oidcClient.token.exchange(code, state); +if ('error' in tokens) { + console.error('Token exchange failed:', tokens.error); +} else { + console.log('Access token:', tokens.accessToken); } ``` @@ -320,7 +351,7 @@ if (node.status === 'failure') { renderError(error); // ... user clicks button to restart flow - const freshNode = davinciClient.start(); + const freshNode = await davinciClient.start(); } ``` diff --git a/packages/davinci-client/api-report/davinci-client.api.md b/packages/davinci-client/api-report/davinci-client.api.md index f324e08d857..d7a4b1395cd 100644 --- a/packages/davinci-client/api-report/davinci-client.api.md +++ b/packages/davinci-client/api-report/davinci-client.api.md @@ -1,2443 +1,2025 @@ -## API Report File for "@forgerock/davinci-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import type { DaVinciConfig } from '@forgerock/sdk-types'; -import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; -import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; -import type { GenericError } from '@forgerock/sdk-types'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { makeDavinciConfig } from '@forgerock/sdk-utilities'; -import type { MutationResultSelectorResult } from '@reduxjs/toolkit/query'; -import { QueryStatus } from '@reduxjs/toolkit/query'; -import { Reducer } from '@reduxjs/toolkit'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { SerializedError } from '@reduxjs/toolkit'; -import { Unsubscribe } from '@reduxjs/toolkit'; - -// @public (undocumented) -export type ActionCollector = - | ActionCollectorNoUrl - | ActionCollectorWithUrl; - -// @public (undocumented) -export interface ActionCollectorNoUrl { - // (undocumented) - category: 'ActionCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type ActionCollectors = - | ActionCollectorWithUrl<'IdpCollector'> - | ActionCollectorNoUrl<'ActionCollector'> - | ActionCollectorNoUrl<'FlowCollector'> - | ActionCollectorNoUrl<'SubmitCollector'>; - -// @public -export type ActionCollectorTypes = - | 'FlowCollector' - | 'SubmitCollector' - | 'IdpCollector' - | 'ActionCollector'; - -// @public (undocumented) -export interface ActionCollectorWithUrl { - // (undocumented) - category: 'ActionCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - url?: string | null; - }; - // (undocumented) - type: T; -} - -export { ActionTypes }; - -// @public (undocumented) -export type AgreementField = { - type: 'AGREEMENT'; - key: string; - content: string; - titleEnabled: boolean; - title?: string; - agreement: { - id: string; - useDynamicAgreement: boolean; - }; - enabled: boolean; -}; - -// @public (undocumented) -export interface AssertionValue extends Omit< - PublicKeyCredential, - 'rawId' | 'response' | 'getClientExtensionResults' | 'toJSON' -> { - // (undocumented) - rawId: string; - // (undocumented) - response: { - clientDataJSON: string; - authenticatorData: string; - signature: string; - userHandle: string | null; - }; -} - -// @public (undocumented) -export interface AttestationValue extends Omit< - PublicKeyCredential, - 'rawId' | 'response' | 'getClientExtensionResults' | 'toJSON' -> { - // (undocumented) - rawId: string; - // (undocumented) - response: { - clientDataJSON: string; - attestationObject: string; - }; -} - -// @public (undocumented) -export interface AutoCollector< - C extends AutoCollectorCategories, - T extends AutoCollectorTypes, - IV = string, - OV = Record, -> { - // (undocumented) - category: C; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: IV; - type: string; - validation?: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - type: string; - config: OV; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type AutoCollectorCategories = 'SingleValueAutoCollector' | 'ObjectValueAutoCollector'; - -// @public (undocumented) -export type AutoCollectors = - | ProtectCollector - | FidoRegistrationCollector - | FidoAuthenticationCollector - | MetadataCollector - | PollingCollector - | SingleValueAutoCollector - | ObjectValueAutoCollector; - -// @public (undocumented) -export type AutoCollectorTypes = SingleValueAutoCollectorTypes | ObjectValueAutoCollectorTypes; - -// @public (undocumented) -export interface BooleanCollector extends SingleValueCollectorWithValue< - 'BooleanCollector', - boolean -> { - // (undocumented) - output: SingleValueCollectorWithValue<'BooleanCollector', boolean>['output'] & { - appearance: string; - richContent?: CollectorRichContent; - }; -} - -// @public (undocumented) -export type CollectorCategory = Collectors['category']; - -// @public (undocumented) -export interface CollectorErrors { - // (undocumented) - code: string; - // (undocumented) - message: string; - // (undocumented) - target: string; -} - -// @public -export interface CollectorRichContent { - // (undocumented) - content: string; - // (undocumented) - replacements: RichContentLink[]; -} - -// @public (undocumented) -export type Collectors = - | FlowCollector - | MetadataCollector - | PasswordCollector - | ValidatedPasswordCollector - | TextCollector - | BooleanCollector - | ValidatedBooleanCollector - | SingleSelectCollector - | IdpCollector - | SubmitCollector - | ActionCollector<'ActionCollector'> - | SingleValueCollector<'SingleValueCollector'> - | MultiSelectCollector - | DeviceAuthenticationCollector - | DeviceRegistrationCollector - | PhoneNumberCollector - | PhoneNumberExtensionCollector - | ReadOnlyCollector - | RichTextCollector - | ValidatedTextCollector - | ProtectCollector - | PollingCollector - | FidoRegistrationCollector - | FidoAuthenticationCollector - | QrCodeCollector - | ImageCollector - | UnknownCollector; - -// @public -export type CollectorValueType = T extends - | { - type: 'PasswordCollector'; - } - | { - type: 'ValidatedPasswordCollector'; - } - | { - type: 'SingleSelectCollector'; - } - | { - type: 'DeviceRegistrationCollector'; - } - | { - type: 'DeviceAuthenticationCollector'; - } - | { - type: 'ProtectCollector'; - } - | { - type: 'PollingCollector'; - } - ? string - : T extends { - type: 'TextCollector'; - category: 'SingleValueCollector'; - } - ? string - : T extends { - type: 'TextCollector'; - category: 'ValidatedSingleValueCollector'; - } - ? string - : T extends - | { - type: 'BooleanCollector'; - } - | { - type: 'ValidatedBooleanCollector'; - } - ? boolean - : T extends { - type: 'MultiSelectCollector'; - } - ? string | string[] - : T extends { - type: 'PhoneNumberCollector'; - } - ? PhoneNumberInputValue - : T extends { - type: 'PhoneNumberExtensionCollector'; - } - ? PhoneNumberExtensionInputValue - : T extends { - type: 'FidoRegistrationCollector'; - } - ? FidoRegistrationInputValue | GenericError - : T extends { - type: 'FidoAuthenticationCollector'; - } - ? FidoAuthenticationInputValue | GenericError - : T extends { - type: 'MetadataCollector'; - } - ? Record | MetadataError - : T extends { - category: 'SingleValueCollector'; - } - ? string - : T extends { - category: 'ValidatedSingleValueCollector'; - } - ? string - : T extends { - category: 'SingleValueAutoCollector'; - } - ? string - : T extends { - category: 'MultiValueCollector'; - } - ? string | string[] - : T extends { - category: 'ActionCollector'; - } - ? never - : T extends { - category: 'NoValueCollector'; - } - ? never - : CollectorValueTypes; - -// @public -export type CollectorValueTypes = - | string - | string[] - | boolean - | PhoneNumberInputValue - | PhoneNumberExtensionInputValue - | FidoRegistrationInputValue - | FidoAuthenticationInputValue - | MetadataError - | GenericError; - -// @public (undocumented) -export type ComplexValueFields = - | DeviceAuthenticationField - | DeviceRegistrationField - | PhoneNumberField - | PhoneNumberExtensionField - | FidoRegistrationField - | FidoAuthenticationField - | PollingField - | MetadataField; - -// @public (undocumented) -export interface ContinueNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'continue'; - }; - // (undocumented) - error: null; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - eventName?: string; - status: 'continue'; - }; - // (undocumented) - status: 'continue'; -} - -export { CustomLogger }; - -// @public -export type CustomPollingStatus = string & {}; - -// @public -export function davinci(input: { - config: DaVinciConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; -}): Promise<{ - subscribe: (listener: () => void) => Unsubscribe; - externalIdp: () => () => Promise; - flow: (action: DaVinciAction) => InitFlow; - next: (args?: DaVinciRequest) => Promise; - resume: (input: { continueToken: string }) => Promise; - start: ( - options?: StartOptions | undefined, - ) => Promise; - update: (collector: T) => Updater; - validate: ( - collector: - | SingleValueCollectors - | ObjectValueCollectors - | MultiValueCollectors - | AutoCollectors, - ) => Validator; - pollStatus: (collector: PollingCollector) => Poller; - getClient: () => - | { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'continue'; - } - | { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'error'; - } - | { - status: 'failure'; - } - | { - status: 'start'; - } - | { - authorization?: { - code?: string; - state?: string; - }; - status: 'success'; - } - | null; - getCollectors: () => Collectors[]; - getError: () => DaVinciError | null; - getErrorCollectors: () => CollectorErrors[]; - getNode: () => ContinueNode | ErrorNode | FailureNode | StartNode | SuccessNode; - getServer: () => - | { - _links?: Links; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - eventName?: string; - status: 'continue'; - } - | { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'error'; - } - | { - _links?: Links; - eventName?: string; - href?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'failure'; - } - | { - status: 'start'; - } - | { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - session?: string; - status: 'success'; - } - | null; - cache: { - getLatestResponse: () => - | ({ - status: QueryStatus.fulfilled; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > - > & { - error: undefined; - } & { - status: QueryStatus.fulfilled; - isUninitialized: false; - isLoading: false; - isSuccess: true; - isError: false; - }) - | ({ - status: QueryStatus.rejected; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > - > & { - status: QueryStatus.rejected; - isUninitialized: false; - isLoading: false; - isSuccess: false; - isError: true; - }) - | { - error: { - message: string; - type: string; - }; - }; - getResponseWithId: (requestId: string) => - | ({ - status: QueryStatus.fulfilled; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > - > & { - error: undefined; - } & { - status: QueryStatus.fulfilled; - isUninitialized: false; - isLoading: false; - isSuccess: true; - isError: false; - }) - | ({ - status: QueryStatus.rejected; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > - > & { - status: QueryStatus.rejected; - isUninitialized: false; - isLoading: false; - isSuccess: false; - isError: true; - }) - | { - error: { - message: string; - type: string; - }; - }; - }; -}>; - -// @public -export interface DaVinciAction { - // (undocumented) - action: string; -} - -// @public -export interface DaVinciBaseResponse { - // (undocumented) - capabilityName?: string; - // (undocumented) - companyId?: string; - // (undocumented) - connectionId?: string; - // (undocumented) - connectorId?: string; - // (undocumented) - id?: string; - // (undocumented) - interactionId?: string; - // (undocumented) - interactionToken?: string; - // (undocumented) - isResponseCompatibleWithMobileAndWebSdks?: boolean; - // (undocumented) - status?: string; -} - -// @public -export type DaVinciCacheEntry = { - data?: DaVinciBaseResponse; - error?: { - data: DaVinciBaseResponse; - status: number; - }; -} & { - data?: any; - error?: any; -} & MutationResultSelectorResult; - -// @public (undocumented) -export type DavinciClient = Awaited>; - -export { DaVinciConfig }; - -// @public (undocumented) -export interface DaVinciError extends Omit { - // (undocumented) - collectors?: CollectorErrors[]; - // (undocumented) - internalHttpStatus?: number; - // (undocumented) - message: string; - // (undocumented) - status: 'error' | 'failure' | 'unknown'; -} - -// @public (undocumented) -export interface DaVinciErrorCacheEntry { - // (undocumented) - endpointName: 'next' | 'flow' | 'start'; - // (undocumented) - error: { - data: T; - }; - // (undocumented) - fulfilledTimeStamp: number; - // (undocumented) - isError: boolean; - // (undocumented) - isLoading: boolean; - // (undocumented) - isSuccess: boolean; - // (undocumented) - isUninitialized: boolean; - // (undocumented) - requestId: string; - // (undocumented) - startedTimeStamp: number; - // (undocumented) - status: 'fulfilled' | 'pending' | 'rejected'; -} - -// @public (undocumented) -export interface DavinciErrorResponse extends DaVinciBaseResponse { - // (undocumented) - cause?: string | null; - // (undocumented) - code: string | number; - // (undocumented) - details?: ErrorDetail[]; - // (undocumented) - doNotSendToOE?: boolean; - // (undocumented) - error?: { - code?: string; - message?: string; - }; - // (undocumented) - errorCategory?: string; - // (undocumented) - errorMessage?: string; - // (undocumented) - expected?: boolean; - // (undocumented) - httpResponseCode: number; - // (undocumented) - isErrorCustomized?: boolean; - // (undocumented) - message: string; - // (undocumented) - metricAttributes?: { - [key: string]: unknown; - }; -} - -// @public (undocumented) -export interface DaVinciFailureResponse extends DaVinciBaseResponse { - // (undocumented) - error?: { - code?: string; - message?: string; - [key: string]: unknown; - }; -} - -// @public (undocumented) -export type DaVinciField = - | ComplexValueFields - | MultiValueFields - | ReadOnlyFields - | RedirectFields - | SingleValueFields; - -// @public -export interface DaVinciNextResponse extends DaVinciBaseResponse { - // (undocumented) - eventName?: string; - // (undocumented) - form?: { - name?: string; - description?: string; - components?: { - fields?: DaVinciField[]; - }; - }; - // (undocumented) - formData?: { - value?: { - [key: string]: string; - }; - }; - // (undocumented) - _links?: Links; -} - -// @public -export interface DaVinciPollResponse extends DaVinciBaseResponse { - // (undocumented) - eventName?: string; - // (undocumented) - _links?: Links; - // (undocumented) - success?: boolean; -} - -// @public (undocumented) -export interface DaVinciRequest { - // (undocumented) - eventName: string; - // (undocumented) - id: string; - // (undocumented) - interactionId: string; - // (undocumented) - parameters: { - eventType: 'submit' | 'action' | 'polling'; - data: { - actionKey: string; - formData?: Record; - }; - }; -} - -// @public -export type DaVinciRequestValueTypes = - | string - | number - | boolean - | (string | number | boolean)[] - | DeviceValue - | PhoneNumberInputValue - | FidoRegistrationInputValue - | FidoAuthenticationInputValue - | MetadataError - | GenericError; - -// @public (undocumented) -export interface DaVinciSuccessResponse extends DaVinciBaseResponse { - // (undocumented) - authorizeResponse?: OAuthDetails; - // (undocumented) - environment: { - id: string; - [key: string]: unknown; - }; - // (undocumented) - _links?: Links; - // (undocumented) - resetCookie?: boolean; - // (undocumented) - session?: { - id?: string; - [key: string]: unknown; - }; - // (undocumented) - sessionToken?: string; - // (undocumented) - sessionTokenMaxAge?: number; - // (undocumented) - status: string; - // (undocumented) - subFlowSettings?: { - cssLinks?: unknown[]; - cssUrl?: unknown; - jsLinks?: unknown[]; - loadingScreenSettings?: unknown; - reactSkUrl?: unknown; - }; - // (undocumented) - success: true; -} - -// @public (undocumented) -export type DeviceAuthenticationCollector = ObjectOptionsCollectorWithObjectValue< - 'DeviceAuthenticationCollector', - DeviceValue ->; - -// @public (undocumented) -export type DeviceAuthenticationField = { - type: 'DEVICE_AUTHENTICATION'; - key: string; - label: string; - options: { - type: string; - iconSrc: string; - title: string; - id: string; - default: boolean; - description: string; - }[]; - required: boolean; -}; - -// @public (undocumented) -export interface DeviceOptionNoDefault { - // (undocumented) - content: string; - // (undocumented) - key: string; - // (undocumented) - label: string; - // (undocumented) - type: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export interface DeviceOptionWithDefault { - // (undocumented) - content: string; - // (undocumented) - default: boolean; - // (undocumented) - key: string; - // (undocumented) - label: string; - // (undocumented) - type: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export type DeviceRegistrationCollector = ObjectOptionsCollectorWithStringValue< - 'DeviceRegistrationCollector', - string ->; - -// @public (undocumented) -export type DeviceRegistrationField = { - type: 'DEVICE_REGISTRATION'; - key: string; - label: string; - options: { - type: string; - iconSrc: string; - title: string; - description: string; - }[]; - required: boolean; -}; - -// @public (undocumented) -export interface DeviceValue { - // (undocumented) - id: string; - // (undocumented) - type: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export interface ErrorDetail { - // (undocumented) - message?: string; - // (undocumented) - rawResponse?: { - _embedded?: { - users?: Array; - }; - code?: string; - count?: number; - details?: NestedErrorDetails[]; - id?: string; - message?: string; - size?: number; - userFilter?: string; - [key: string]: unknown; - }; - // (undocumented) - statusCode?: number; -} - -// @public (undocumented) -export interface ErrorNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'error'; - }; - // (undocumented) - error: DaVinciError; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'error'; - } | null; - // (undocumented) - status: 'error'; -} - -// @public (undocumented) -export interface FailureNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - status: 'failure'; - }; - // (undocumented) - error: DaVinciError; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - eventName?: string; - href?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'failure'; - } | null; - // (undocumented) - status: 'failure'; -} - -// @public -export function fido(): FidoClient; - -// @public (undocumented) -export type FidoAuthenticationCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'FidoAuthenticationCollector', - FidoAuthenticationInputValue | GenericError, - FidoAuthenticationOutputValue ->; - -// @public (undocumented) -export type FidoAuthenticationField = { - type: 'FIDO2'; - key: string; - label: string; - publicKeyCredentialRequestOptions: FidoAuthenticationOptions; - action: 'AUTHENTICATE'; - trigger: string; - required: boolean; -}; - -// @public (undocumented) -export interface FidoAuthenticationInputValue { - // (undocumented) - assertionValue?: AssertionValue; -} - -// @public (undocumented) -export interface FidoAuthenticationOptions extends Omit< - PublicKeyCredentialRequestOptions, - 'challenge' | 'allowCredentials' -> { - // (undocumented) - allowCredentials?: { - id: number[]; - transports?: AuthenticatorTransport[]; - type: PublicKeyCredentialType; - }[]; - // (undocumented) - challenge: number[]; -} - -// @public (undocumented) -export interface FidoAuthenticationOutputValue { - // (undocumented) - action: 'AUTHENTICATE'; - // (undocumented) - publicKeyCredentialRequestOptions: FidoAuthenticationOptions; - // (undocumented) - trigger: string; -} - -// @public (undocumented) -export interface FidoClient { - authenticate: ( - options: FidoAuthenticationOptions, - ) => Promise; - register: ( - options: FidoRegistrationOptions, - ) => Promise; -} - -// @public -export type FidoErrorCode = - | 'NotAllowedError' - | 'AbortError' - | 'InvalidStateError' - | 'NotSupportedError' - | 'SecurityError' - | 'TimeoutError' - | 'UnknownError'; - -// @public (undocumented) -export type FidoRegistrationCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'FidoRegistrationCollector', - FidoRegistrationInputValue | GenericError, - FidoRegistrationOutputValue ->; - -// @public (undocumented) -export type FidoRegistrationField = { - type: 'FIDO2'; - key: string; - label: string; - publicKeyCredentialCreationOptions: FidoRegistrationOptions; - action: 'REGISTER'; - trigger: string; - required: boolean; -}; - -// @public (undocumented) -export interface FidoRegistrationInputValue { - // (undocumented) - attestationValue?: AttestationValue; -} - -// @public (undocumented) -export interface FidoRegistrationOptions extends Omit< - PublicKeyCredentialCreationOptions, - 'challenge' | 'user' | 'pubKeyCredParams' | 'excludeCredentials' -> { - // (undocumented) - challenge: number[]; - // (undocumented) - excludeCredentials?: { - id: number[]; - transports?: AuthenticatorTransport[]; - type: PublicKeyCredentialType; - }[]; - // (undocumented) - pubKeyCredParams: { - alg: string | number; - type: PublicKeyCredentialType; - }[]; - // (undocumented) - user: { - id: number[]; - name: string; - displayName: string; - }; -} - -// @public (undocumented) -export interface FidoRegistrationOutputValue { - // (undocumented) - action: 'REGISTER'; - // (undocumented) - publicKeyCredentialCreationOptions: FidoRegistrationOptions; - // (undocumented) - trigger: string; -} - -// @public (undocumented) -export type FlowCollector = ActionCollectorNoUrl<'FlowCollector'>; - -// @public (undocumented) -export type FlowNode = ContinueNode | ErrorNode | StartNode | SuccessNode | FailureNode; - -// @public -export type GetClient = - | StartNode['client'] - | ContinueNode['client'] - | ErrorNode['client'] - | SuccessNode['client'] - | FailureNode['client']; - -// @public (undocumented) -export type IdpCollector = ActionCollectorWithUrl<'IdpCollector'>; - -// @public -export interface ImageCollector extends NoValueCollectorBase<'ImageCollector'> { - // (undocumented) - output: NoValueCollectorBase<'ImageCollector'>['output'] & { - src: string; - alt: string; - href?: string; - }; -} - -// @public (undocumented) -export type ImageField = { - type: 'IMAGE'; - key: string; - description: string; - imageUrl: string; - hyperlinkUrl?: string; -}; - -// @public (undocumented) -export type InferActionCollectorType = T extends 'IdpCollector' - ? IdpCollector - : T extends 'SubmitCollector' - ? SubmitCollector - : T extends 'FlowCollector' - ? FlowCollector - : ActionCollectorWithUrl<'ActionCollector'> | ActionCollectorNoUrl<'ActionCollector'>; - -// @public -export type InferAutoCollectorType = T extends 'ProtectCollector' - ? ProtectCollector - : T extends 'PollingCollector' - ? PollingCollector - : T extends 'FidoRegistrationCollector' - ? FidoRegistrationCollector - : T extends 'FidoAuthenticationCollector' - ? FidoAuthenticationCollector - : T extends 'MetadataCollector' - ? MetadataCollector - : T extends 'ObjectValueAutoCollector' - ? ObjectValueAutoCollector - : SingleValueAutoCollector; - -// @public -export type InferMultiValueCollectorType = - T extends 'MultiSelectCollector' - ? MultiValueCollectorWithValue<'MultiSelectCollector'> - : - | MultiValueCollectorWithValue<'MultiValueCollector'> - | MultiValueCollectorNoValue<'MultiValueCollector'>; - -// @public -export type InferNoValueCollectorType = - T extends 'ReadOnlyCollector' - ? ReadOnlyCollector - : T extends 'RichTextCollector' - ? RichTextCollector - : T extends 'QrCodeCollector' - ? QrCodeCollector - : T extends 'ImageCollector' - ? ImageCollector - : NoValueCollectorBase<'NoValueCollector'>; - -// @public -export type InferSingleValueCollectorType = - T extends 'TextCollector' - ? TextCollector - : T extends 'SingleSelectCollector' - ? SingleSelectCollector - : T extends 'ValidatedTextCollector' - ? ValidatedTextCollector - : T extends 'PasswordCollector' - ? PasswordCollector - : T extends 'ValidatedPasswordCollector' - ? ValidatedPasswordCollector - : T extends 'BooleanCollector' - ? BooleanCollector - : T extends 'ValidatedBooleanCollector' - ? ValidatedBooleanCollector - : - | SingleValueCollectorWithValue<'SingleValueCollector'> - | SingleValueCollectorNoValue<'SingleValueCollector'>; - -// @public (undocumented) -export type InferValueObjectCollectorType = - T extends 'DeviceAuthenticationCollector' - ? DeviceAuthenticationCollector - : T extends 'DeviceRegistrationCollector' - ? DeviceRegistrationCollector - : T extends 'PhoneNumberCollector' - ? PhoneNumberCollector - : T extends 'PhoneNumberExtensionCollector' - ? PhoneNumberExtensionCollector - : - | ObjectOptionsCollectorWithObjectValue<'ObjectValueCollector'> - | ObjectOptionsCollectorWithStringValue<'ObjectValueCollector'>; - -// @public (undocumented) -export type InitFlow = () => Promise; - -// @public (undocumented) -export interface InternalErrorResponse { - // (undocumented) - error: Omit & { - message: string; - }; - // (undocumented) - type: 'internal_error'; -} - -// @public (undocumented) -export interface Links { - // (undocumented) - [key: string]: { - href?: string; - }; -} - -export { LogLevel }; - -export { makeDavinciConfig }; - -// @public (undocumented) -export type MetadataCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'MetadataCollector', - MetadataCollectorInputValue, - Record ->; - -// @public (undocumented) -export type MetadataCollectorInputValue = Record | MetadataError; - -// @public -export interface MetadataError { - // (undocumented) - code: string; - // (undocumented) - message: string; -} - -// @public (undocumented) -export type MetadataField = { - type: 'METADATA'; - key: string; - payload: Record; -}; - -// @public (undocumented) -export type MultiSelectCollector = MultiValueCollectorWithValue<'MultiSelectCollector'>; - -// @public (undocumented) -export type MultiSelectField = { - inputType: 'MULTI_SELECT'; - key: string; - label: string; - options: { - label: string; - value: string; - }[]; - required?: boolean; - type: 'CHECKBOX' | 'COMBOBOX'; -}; - -// @public (undocumented) -export type MultiValueCollector = - | MultiValueCollectorWithValue - | MultiValueCollectorNoValue; - -// @public (undocumented) -export interface MultiValueCollectorNoValue { - // (undocumented) - category: 'MultiValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string[]; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type MultiValueCollectors = - | MultiValueCollectorWithValue<'MultiValueCollector'> - | MultiValueCollectorWithValue<'MultiSelectCollector'>; - -// @public -export type MultiValueCollectorTypes = 'MultiSelectCollector' | 'MultiValueCollector'; - -// @public (undocumented) -export interface MultiValueCollectorWithValue { - // (undocumented) - category: 'MultiValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string[]; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: string[]; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type MultiValueFields = MultiSelectField; - -// @public -export interface NestedErrorDetails { - // (undocumented) - code?: string; - // (undocumented) - innerError?: { - history?: string; - unsatisfiedRequirements?: string[]; - failuresRemaining?: number; - }; - // (undocumented) - message?: string; - // (undocumented) - target?: string; -} - -// @public -export const nextCollectorValues: ActionCreatorWithPayload< - { - fields: DaVinciField[]; - formData: { - value: Record; - }; - }, - string ->; - -// @public -export const nodeCollectorReducer: Reducer & { - getInitialState: () => Collectors[]; -}; - -// @public (undocumented) -export type NodeStates = StartNode | ContinueNode | ErrorNode | SuccessNode | FailureNode; - -// @public (undocumented) -export type NoValueCollector = InferNoValueCollectorType; - -// @public (undocumented) -export interface NoValueCollectorBase { - // (undocumented) - category: 'NoValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type NoValueCollectors = - | NoValueCollectorBase<'NoValueCollector'> - | ReadOnlyCollector - | RichTextCollector - | QrCodeCollector - | ImageCollector; - -// @public -export type NoValueCollectorTypes = - | 'ReadOnlyCollector' - | 'RichTextCollector' - | 'NoValueCollector' - | 'QrCodeCollector' - | 'ImageCollector'; - -// @public -export interface OAuthDetails { - // (undocumented) - [key: string]: unknown; - // (undocumented) - code?: string; - // (undocumented) - state?: string; -} - -// @public (undocumented) -export interface ObjectOptionsCollectorWithObjectValue< - T extends ObjectValueCollectorTypes, - V = Record, - D = Record, -> { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: V; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: DeviceOptionWithDefault[]; - value?: D | null; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export interface ObjectOptionsCollectorWithStringValue< - T extends ObjectValueCollectorTypes, - V = string, -> { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: V; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: DeviceOptionNoDefault[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type ObjectValueAutoCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'ObjectValueAutoCollector', - Record ->; - -// @public (undocumented) -export type ObjectValueAutoCollectorTypes = - | 'ObjectValueAutoCollector' - | 'FidoRegistrationCollector' - | 'FidoAuthenticationCollector' - | 'MetadataCollector'; - -// @public (undocumented) -export type ObjectValueCollector = - | ObjectOptionsCollectorWithObjectValue - | ObjectOptionsCollectorWithStringValue - | ObjectValueCollectorWithObjectValue; - -// @public (undocumented) -export type ObjectValueCollectors = - | DeviceAuthenticationCollector - | DeviceRegistrationCollector - | PhoneNumberCollector - | PhoneNumberExtensionCollector - | ObjectOptionsCollectorWithObjectValue<'ObjectSelectCollector'> - | ObjectOptionsCollectorWithStringValue<'ObjectSelectCollector'>; - -// @public -export type ObjectValueCollectorTypes = - | 'DeviceAuthenticationCollector' - | 'DeviceRegistrationCollector' - | 'PhoneNumberCollector' - | 'PhoneNumberExtensionCollector' - | 'ObjectOptionsCollector' - | 'ObjectValueCollector' - | 'ObjectSelectCollector'; - -// @public (undocumented) -export interface ObjectValueCollectorWithObjectValue< - T extends ObjectValueCollectorTypes, - IV = Record, - OV = Record, -> { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: IV; - type: string; - validation: (ValidationRequired | ValidationPhoneNumber)[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value?: OV | null; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export interface OutgoingQueryParams { - // (undocumented) - [key: string]: string | string[]; -} - -// @public (undocumented) -export interface PasswordCollector { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - verify: boolean; - }; - // (undocumented) - type: 'PasswordCollector'; -} - -// @public -export type PasswordField = { - type: 'PASSWORD' | 'PASSWORD_VERIFY'; - key: string; - label: string; - required?: boolean; - verify?: boolean; - passwordPolicy?: PasswordPolicy; -}; - -// @public -export interface PasswordPolicy { - // (undocumented) - createdAt?: string; - // (undocumented) - default?: boolean; - // (undocumented) - description?: string; - // (undocumented) - excludesCommonlyUsed?: boolean; - // (undocumented) - excludesProfileData?: boolean; - // (undocumented) - history?: { - count?: number; - retentionDays?: number; - }; - // (undocumented) - id?: string; - // (undocumented) - length?: { - min?: number; - max?: number; - }; - // (undocumented) - lockout?: { - failureCount?: number; - durationSeconds?: number; - }; - // (undocumented) - maxAgeDays?: number; - // (undocumented) - maxRepeatedCharacters?: number; - // (undocumented) - minAgeDays?: number; - // (undocumented) - minCharacters?: Record; - // (undocumented) - minUniqueCharacters?: number; - // (undocumented) - name?: string; - // (undocumented) - notSimilarToCurrent?: boolean; - // (undocumented) - populationCount?: number; - // (undocumented) - updatedAt?: string; -} - -// @public (undocumented) -export type PhoneNumberCollector = ObjectValueCollectorWithObjectValue< - 'PhoneNumberCollector', - PhoneNumberInputValue, - PhoneNumberOutputValue ->; - -// @public (undocumented) -export interface PhoneNumberExtensionCollector { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: PhoneNumberExtensionInputValue; - type: string; - validation: (ValidationRequired | ValidationPhoneNumber)[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - extensionLabel: string; - value: PhoneNumberExtensionOutputValue; - }; - // (undocumented) - type: 'PhoneNumberExtensionCollector'; -} - -// @public (undocumented) -export type PhoneNumberExtensionField = PhoneNumberField & { - showExtension: boolean; - extensionLabel: string; -}; - -// @public (undocumented) -export interface PhoneNumberExtensionInputValue { - // (undocumented) - countryCode: string; - // (undocumented) - extension: string; - // (undocumented) - phoneNumber: string; -} - -// @public (undocumented) -export interface PhoneNumberExtensionOutputValue { - // (undocumented) - countryCode?: string; - // (undocumented) - extension?: string; - // (undocumented) - phoneNumber?: string; -} - -// @public (undocumented) -export type PhoneNumberField = { - type: 'PHONE_NUMBER'; - key: string; - label: string; - required: boolean; - defaultCountryCode: string | null; - validatePhoneNumber: boolean; -}; - -// @public (undocumented) -export interface PhoneNumberInputValue { - // (undocumented) - countryCode: string; - // (undocumented) - phoneNumber: string; -} - -// @public (undocumented) -export interface PhoneNumberOutputValue { - // (undocumented) - countryCode?: string; - // (undocumented) - phoneNumber?: string; -} - -// @public -export type Poller = () => Promise; - -// @public (undocumented) -export type PollingCollector = AutoCollector< - 'SingleValueAutoCollector', - 'PollingCollector', - string, - PollingOutputValue ->; - -// @public (undocumented) -export type PollingField = { - type: 'POLLING'; - key: string; - pollInterval: number; - pollRetries: number; - pollChallengeStatus?: boolean; - challenge?: string; -}; - -// @public (undocumented) -export interface PollingOutputValue { - // (undocumented) - challenge?: string; - // (undocumented) - pollChallengeStatus?: boolean; - // (undocumented) - pollInterval: number; - // (undocumented) - pollRetries: number; - // (undocumented) - retriesRemaining?: number; -} - -// @public (undocumented) -export type PollingStatus = PollingStatusContinue | PollingStatusChallenge; - -// @public (undocumented) -export type PollingStatusChallenge = - | PollingStatusChallengeComplete - | 'expired' - | 'timedOut' - | 'error'; - -// @public (undocumented) -export type PollingStatusChallengeComplete = - | 'approved' - | 'denied' - | 'continue' - | CustomPollingStatus; - -// @public (undocumented) -export type PollingStatusContinue = 'continue' | 'timedOut'; - -// @public (undocumented) -export type ProtectCollector = AutoCollector< - 'SingleValueAutoCollector', - 'ProtectCollector', - string, - ProtectOutputValue ->; - -// @public (undocumented) -export type ProtectField = { - type: 'PROTECT'; - key: string; - behavioralDataCollection: boolean; - universalDeviceIdentification: boolean; -}; - -// @public -export interface ProtectOutputValue { - // (undocumented) - behavioralDataCollection: boolean; - // (undocumented) - universalDeviceIdentification: boolean; -} - -// @public -export interface QrCodeCollector extends NoValueCollectorBase<'QrCodeCollector'> { - // (undocumented) - output: NoValueCollectorBase<'QrCodeCollector'>['output'] & { - src: string; - }; -} - -// @public (undocumented) -export type QrCodeField = { - type: 'QR_CODE'; - key: string; - content: string; - fallbackText?: string; -}; - -// @public -export interface ReadOnlyCollector extends NoValueCollectorBase<'ReadOnlyCollector'> { - // (undocumented) - output: NoValueCollectorBase<'ReadOnlyCollector'>['output'] & { - content: string; - title?: string; - }; -} - -// @public (undocumented) -export type ReadOnlyField = { - type: 'LABEL'; - content: string; - richContent?: RichContent; - key?: string; -}; - -// @public (undocumented) -export type ReadOnlyFields = ReadOnlyField | QrCodeField | AgreementField | ImageField; - -// @public (undocumented) -export type RedirectField = { - type: 'SOCIAL_LOGIN_BUTTON'; - key: string; - label: string; - links: Links; -}; - -// @public (undocumented) -export type RedirectFields = RedirectField; - -export { RequestMiddleware }; - -// @public -export type RichContent = { - content: string; - replacements?: Record; -}; - -// @public -export interface RichContentLink { - // (undocumented) - href: string; - // (undocumented) - key: string; - // (undocumented) - target?: '_self' | '_blank'; - // (undocumented) - type: 'link'; - // (undocumented) - value: string; -} - -// @public -export type RichContentReplacement = { - type: 'link'; - value: string; - href: string; - target?: '_self' | '_blank'; -}; - -// @public -export interface RichTextCollector extends NoValueCollectorBase<'RichTextCollector'> { - // (undocumented) - output: NoValueCollectorBase<'RichTextCollector'>['output'] & { - content: string; - richContent: CollectorRichContent; - }; -} - -// @public (undocumented) -export interface SelectorOption { - // (undocumented) - label: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export type SingleCheckboxField = { - type: 'SINGLE_CHECKBOX'; - inputType: 'BOOLEAN'; - key: string; - label: string; - required?: boolean; - errorMessage?: string; - appearance: string; - richContent?: RichContent; -}; - -// @public (undocumented) -export type SingleSelectCollector = SingleSelectCollectorWithValue<'SingleSelectCollector'>; - -// @public (undocumented) -export interface SingleSelectCollectorNoValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export interface SingleSelectCollectorWithValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: string | number | boolean; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type SingleSelectField = { - inputType: 'SINGLE_SELECT'; - key: string; - label: string; - options: { - label: string; - value: string; - }[]; - required?: boolean; - type: 'RADIO' | 'DROPDOWN'; -}; - -// @public (undocumented) -export type SingleValueAutoCollector = AutoCollector< - 'SingleValueAutoCollector', - 'SingleValueAutoCollector', - string ->; - -// @public (undocumented) -export type SingleValueAutoCollectorTypes = - | 'SingleValueAutoCollector' - | 'ProtectCollector' - | 'PollingCollector'; - -// @public -export type SingleValueCollector = - | SingleValueCollectorWithValue - | SingleValueCollectorNoValue; - -// @public (undocumented) -export interface SingleValueCollectorNoValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type SingleValueCollectors = - | PasswordCollector - | ValidatedPasswordCollector - | SingleSelectCollector - | TextCollector - | ValidatedTextCollector - | BooleanCollector - | ValidatedBooleanCollector - | SingleValueCollectorWithValue<'SingleValueCollector'>; - -// @public -export type SingleValueCollectorTypes = - | 'PasswordCollector' - | 'ValidatedPasswordCollector' - | 'BooleanCollector' - | 'ValidatedBooleanCollector' - | 'SingleValueCollector' - | 'SingleSelectCollector' - | 'SingleSelectObjectCollector' - | 'TextCollector' - | 'ValidatedTextCollector'; - -// @public (undocumented) -export interface SingleValueCollectorWithValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: V; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: V; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type SingleValueFields = - | StandardField - | PasswordField - | ValidatedField - | SingleCheckboxField - | SingleSelectField - | ProtectField; - -// @public (undocumented) -export type StandardField = { - type: 'TEXT' | 'SUBMIT_BUTTON' | 'FLOW_BUTTON' | 'FLOW_LINK' | 'BUTTON'; - key: string; - label: string; - required?: boolean; -}; - -// @public (undocumented) -export interface StartNode { - // (undocumented) - cache: null; - // (undocumented) - client: { - status: 'start'; - }; - // (undocumented) - error: DaVinciError | null; - // (undocumented) - server: { - status: 'start'; - }; - // (undocumented) - status: 'start'; -} - -// @public (undocumented) -export interface StartOptions { - // (undocumented) - query: Query; -} - -// @public (undocumented) -export type SubmitCollector = ActionCollectorNoUrl<'SubmitCollector'>; - -// @public (undocumented) -export interface SuccessNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - authorization?: { - code?: string; - state?: string; - }; - status: 'success'; - } | null; - // (undocumented) - error: null; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - session?: string; - status: 'success'; - }; - // (undocumented) - status: 'success'; -} - -// @public (undocumented) -export type TextCollector = SingleValueCollectorWithValue<'TextCollector'>; - -// @public (undocumented) -export interface ThrownQueryError { - // (undocumented) - error: FetchBaseQueryError; - // (undocumented) - isHandledError: boolean; - // (undocumented) - meta: FetchBaseQueryMeta; -} - -// @public -export type UnknownCollector = { - category: 'UnknownCollector'; - error: string | null; - type: 'UnknownCollector'; - id: string; - name: string; - output: { - key: string; - label: string; - type: string; - }; -}; - -// @public (undocumented) -export type UnknownField = Record; - -// @public -export type UpdatableCollectors = - | SingleValueCollectors - | MultiSelectCollector - | ObjectValueCollectors - | AutoCollectors; - -// @public (undocumented) -export const updateCollectorValues: ActionCreatorWithPayload< - { - id: string; - value: CollectorValueTypes; - index?: number; - }, - string ->; - -// @public -export type Updater = ( - value: CollectorValueType, - index?: number, -) => InternalErrorResponse | null; - -// @public (undocumented) -export interface ValidatedBooleanCollector extends ValidatedSingleValueCollectorWithValue< - 'ValidatedBooleanCollector', - boolean -> { - // (undocumented) - output: ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean>['output'] & { - appearance: string; - richContent?: CollectorRichContent; - }; -} - -// @public -export type ValidatedCollectors = - | ValidatedTextCollector - | ValidatedBooleanCollector - | ValidatedPasswordCollector - | ObjectValueCollectors - | MultiValueCollectors - | AutoCollectors; - -// @public (undocumented) -export type ValidatedField = { - type: 'TEXT'; - key: string; - label: string; - required: boolean; - validation: { - regex: string; - errorMessage: string; - }; -}; - -// @public (undocumented) -export interface ValidatedPasswordCollector { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - validation: PasswordPolicy; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - verify: boolean; - }; - // (undocumented) - type: 'ValidatedPasswordCollector'; -} - -// @public (undocumented) -export interface ValidatedSingleValueCollectorWithValue< - T extends SingleValueCollectorTypes, - V = string, -> { - // (undocumented) - category: 'ValidatedSingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - type: string; - validation: (ValidationRequired | ValidationRegex)[]; - value: V; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: V; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type ValidatedTextCollector = ValidatedSingleValueCollectorWithValue<'TextCollector'>; - -// @public (undocumented) -export interface ValidationPhoneNumber { - // (undocumented) - message: string; - // (undocumented) - rule: boolean; - // (undocumented) - type: 'validatePhoneNumber'; -} - -// @public (undocumented) -export interface ValidationRegex { - // (undocumented) - message: string; - // (undocumented) - rule: string; - // (undocumented) - type: 'regex'; -} - -// @public (undocumented) -export interface ValidationRequired { - // (undocumented) - message: string; - // (undocumented) - rule: boolean; - // (undocumented) - type: 'required'; -} - -// @public -export type Validator = ( - value: CollectorValueType, -) => - | string[] - | { - error: { - message: string; - type: string; - }; - type: string; - }; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/davinci-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import type { DaVinciConfig } from '@forgerock/sdk-types'; +import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; +import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; +import type { GenericError } from '@forgerock/sdk-types'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { makeDavinciConfig } from '@forgerock/sdk-utilities'; +import type { MutationResultSelectorResult } from '@reduxjs/toolkit/query'; +import { QueryStatus } from '@reduxjs/toolkit/query'; +import { Reducer } from '@reduxjs/toolkit'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; +import { SerializedError } from '@reduxjs/toolkit'; +import { Unsubscribe } from '@reduxjs/toolkit'; + +// @public (undocumented) +export type ActionCollector = ActionCollectorNoUrl | ActionCollectorWithUrl; + +// @public (undocumented) +export interface ActionCollectorNoUrl { + // (undocumented) + category: 'ActionCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type ActionCollectors = ActionCollectorWithUrl<'IdpCollector'> | ActionCollectorNoUrl<'ActionCollector'> | ActionCollectorNoUrl<'FlowCollector'> | ActionCollectorNoUrl<'SubmitCollector'>; + +// @public +export type ActionCollectorTypes = 'FlowCollector' | 'SubmitCollector' | 'IdpCollector' | 'ActionCollector'; + +// @public (undocumented) +export interface ActionCollectorWithUrl { + // (undocumented) + category: 'ActionCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + url?: string | null; + }; + // (undocumented) + type: T; +} + +export { ActionTypes } + +// @public (undocumented) +export type AgreementField = { + type: 'AGREEMENT'; + key: string; + content: string; + titleEnabled: boolean; + title?: string; + agreement: { + id: string; + useDynamicAgreement: boolean; + }; + enabled: boolean; +}; + +// @public (undocumented) +export interface AssertionValue extends Omit { + // (undocumented) + rawId: string; + // (undocumented) + response: { + clientDataJSON: string; + authenticatorData: string; + signature: string; + userHandle: string | null; + }; +} + +// @public (undocumented) +export interface AttestationValue extends Omit { + // (undocumented) + rawId: string; + // (undocumented) + response: { + clientDataJSON: string; + attestationObject: string; + }; +} + +// @public (undocumented) +export interface AutoCollector> { + // (undocumented) + category: C; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: IV; + type: string; + validation?: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + type: string; + config: OV; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type AutoCollectorCategories = 'SingleValueAutoCollector' | 'ObjectValueAutoCollector'; + +// @public (undocumented) +export type AutoCollectors = ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | MetadataCollector | PollingCollector | SingleValueAutoCollector | ObjectValueAutoCollector; + +// @public (undocumented) +export type AutoCollectorTypes = SingleValueAutoCollectorTypes | ObjectValueAutoCollectorTypes; + +// @public (undocumented) +export interface BooleanCollector extends SingleValueCollectorWithValue<'BooleanCollector', boolean> { + // (undocumented) + output: SingleValueCollectorWithValue<'BooleanCollector', boolean>['output'] & { + appearance: string; + richContent?: CollectorRichContent; + }; +} + +// @public (undocumented) +export type CollectorCategory = Collectors['category']; + +// @public (undocumented) +export interface CollectorErrors { + // (undocumented) + code: string; + // (undocumented) + message: string; + // (undocumented) + target: string; +} + +// @public +export interface CollectorRichContent { + // (undocumented) + content: string; + // (undocumented) + replacements: RichContentLink[]; +} + +// @public (undocumented) +export type Collectors = FlowCollector | MetadataCollector | PasswordCollector | ValidatedPasswordCollector | TextCollector | BooleanCollector | ValidatedBooleanCollector | SingleSelectCollector | IdpCollector | SubmitCollector | ActionCollector<'ActionCollector'> | SingleValueCollector<'SingleValueCollector'> | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ReadOnlyCollector | RichTextCollector | ValidatedTextCollector | ProtectCollector | PollingCollector | FidoRegistrationCollector | FidoAuthenticationCollector | QrCodeCollector | ImageCollector | UnknownCollector; + +// @public +export type CollectorValueType = T extends { + type: 'PasswordCollector'; +} | { + type: 'ValidatedPasswordCollector'; +} | { + type: 'SingleSelectCollector'; +} | { + type: 'DeviceRegistrationCollector'; +} | { + type: 'DeviceAuthenticationCollector'; +} | { + type: 'ProtectCollector'; +} | { + type: 'PollingCollector'; +} ? string : T extends { + type: 'TextCollector'; + category: 'SingleValueCollector'; +} ? string : T extends { + type: 'TextCollector'; + category: 'ValidatedSingleValueCollector'; +} ? string : T extends { + type: 'BooleanCollector'; +} | { + type: 'ValidatedBooleanCollector'; +} ? boolean : T extends { + type: 'MultiSelectCollector'; +} ? string | string[] : T extends { + type: 'PhoneNumberCollector'; +} ? PhoneNumberInputValue : T extends { + type: 'PhoneNumberExtensionCollector'; +} ? PhoneNumberExtensionInputValue : T extends { + type: 'FidoRegistrationCollector'; +} ? FidoRegistrationInputValue | GenericError : T extends { + type: 'FidoAuthenticationCollector'; +} ? FidoAuthenticationInputValue | GenericError : T extends { + type: 'MetadataCollector'; +} ? Record | MetadataError : T extends { + category: 'SingleValueCollector'; +} ? string : T extends { + category: 'ValidatedSingleValueCollector'; +} ? string : T extends { + category: 'SingleValueAutoCollector'; +} ? string : T extends { + category: 'MultiValueCollector'; +} ? string | string[] : T extends { + category: 'ActionCollector'; +} ? never : T extends { + category: 'NoValueCollector'; +} ? never : CollectorValueTypes; + +// @public +export type CollectorValueTypes = string | string[] | boolean | PhoneNumberInputValue | PhoneNumberExtensionInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue | MetadataError | GenericError; + +// @public (undocumented) +export type ComplexValueFields = DeviceAuthenticationField | DeviceRegistrationField | PhoneNumberField | PhoneNumberExtensionField | FidoRegistrationField | FidoAuthenticationField | PollingField | MetadataField; + +// @public (undocumented) +export interface ContinueNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: 'continue'; + }; + // (undocumented) + error: null; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + eventName?: string; + status: 'continue'; + }; + // (undocumented) + status: 'continue'; +} + +export { CustomLogger } + +// @public +export type CustomPollingStatus = string & {}; + +// @public +export function davinci(input: { + config: DaVinciConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; + store?: unknown; +}): Promise<{ + error: string; + type: "argument_error"; +} | { + store: SdkStore; + subscribe: (listener: () => void) => Unsubscribe; + externalIdp: () => (() => Promise); + flow: (action: DaVinciAction) => InitFlow; + next: (args?: DaVinciRequest) => Promise; + resume: (input: { + continueToken: string; + }) => Promise; + start: (options?: StartOptions | undefined) => Promise; + update: (collector: T) => Updater; + validate: (collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors | AutoCollectors) => Validator; + pollStatus: (collector: PollingCollector) => Poller; + getClient: () => { + status: "start"; + } | { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: "error"; + } | { + status: "failure"; + } | { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: "continue"; + } | { + authorization?: { + code?: string; + state?: string; + }; + status: "success"; + } | null; + getCollectors: () => Collectors[]; + getError: () => DaVinciError | null; + getErrorCollectors: () => CollectorErrors[]; + getNode: () => StartNode | ErrorNode | FailureNode | ContinueNode | SuccessNode; + getServer: () => { + status: "start"; + } | { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: "error"; + } | { + _links?: Links; + eventName?: string; + href?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: "failure"; + } | { + _links?: Links; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + eventName?: string; + status: "continue"; + } | { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + session?: string; + status: "success"; + } | null; + cache: { + getLatestResponse: () => ({ + status: QueryStatus.fulfilled; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "data" | "fulfilledTimeStamp"> & Required> & { + error: undefined; + } & { + status: QueryStatus.fulfilled; + isUninitialized: false; + isLoading: false; + isSuccess: true; + isError: false; + }) | ({ + status: QueryStatus.rejected; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "error"> & Required> & { + status: QueryStatus.rejected; + isUninitialized: false; + isLoading: false; + isSuccess: false; + isError: true; + }) | { + error: { + message: string; + type: string; + }; + }; + getResponseWithId: (requestId: string) => ({ + status: QueryStatus.fulfilled; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "data" | "fulfilledTimeStamp"> & Required> & { + error: undefined; + } & { + status: QueryStatus.fulfilled; + isUninitialized: false; + isLoading: false; + isSuccess: true; + isError: false; + }) | ({ + status: QueryStatus.rejected; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "error"> & Required> & { + status: QueryStatus.rejected; + isUninitialized: false; + isLoading: false; + isSuccess: false; + isError: true; + }) | { + error: { + message: string; + type: string; + }; + }; + }; +}>; + +// @public +export interface DaVinciAction { + // (undocumented) + action: string; +} + +// @public +export interface DaVinciBaseResponse { + // (undocumented) + capabilityName?: string; + // (undocumented) + companyId?: string; + // (undocumented) + connectionId?: string; + // (undocumented) + connectorId?: string; + // (undocumented) + id?: string; + // (undocumented) + interactionId?: string; + // (undocumented) + interactionToken?: string; + // (undocumented) + isResponseCompatibleWithMobileAndWebSdks?: boolean; + // (undocumented) + status?: string; +} + +// @public +export type DaVinciCacheEntry = { + data?: DaVinciBaseResponse; + error?: { + data: DaVinciBaseResponse; + status: number; + }; +} & { + data?: any; + error?: any; +} & MutationResultSelectorResult; + +// @public (undocumented) +export type DavinciClient = Awaited>; + +export { DaVinciConfig } + +// @public (undocumented) +export interface DaVinciError extends Omit { + // (undocumented) + collectors?: CollectorErrors[]; + // (undocumented) + internalHttpStatus?: number; + // (undocumented) + message: string; + // (undocumented) + status: 'error' | 'failure' | 'unknown'; +} + +// @public (undocumented) +export interface DaVinciErrorCacheEntry { + // (undocumented) + endpointName: 'next' | 'flow' | 'start'; + // (undocumented) + error: { + data: T; + }; + // (undocumented) + fulfilledTimeStamp: number; + // (undocumented) + isError: boolean; + // (undocumented) + isLoading: boolean; + // (undocumented) + isSuccess: boolean; + // (undocumented) + isUninitialized: boolean; + // (undocumented) + requestId: string; + // (undocumented) + startedTimeStamp: number; + // (undocumented) + status: 'fulfilled' | 'pending' | 'rejected'; +} + +// @public (undocumented) +export interface DavinciErrorResponse extends DaVinciBaseResponse { + // (undocumented) + cause?: string | null; + // (undocumented) + code: string | number; + // (undocumented) + details?: ErrorDetail[]; + // (undocumented) + doNotSendToOE?: boolean; + // (undocumented) + error?: { + code?: string; + message?: string; + }; + // (undocumented) + errorCategory?: string; + // (undocumented) + errorMessage?: string; + // (undocumented) + expected?: boolean; + // (undocumented) + httpResponseCode: number; + // (undocumented) + isErrorCustomized?: boolean; + // (undocumented) + message: string; + // (undocumented) + metricAttributes?: { + [key: string]: unknown; + }; +} + +// @public (undocumented) +export interface DaVinciFailureResponse extends DaVinciBaseResponse { + // (undocumented) + error?: { + code?: string; + message?: string; + [key: string]: unknown; + }; +} + +// @public (undocumented) +export type DaVinciField = ComplexValueFields | MultiValueFields | ReadOnlyFields | RedirectFields | SingleValueFields; + +// @public +export interface DaVinciNextResponse extends DaVinciBaseResponse { + // (undocumented) + eventName?: string; + // (undocumented) + form?: { + name?: string; + description?: string; + components?: { + fields?: DaVinciField[]; + }; + }; + // (undocumented) + formData?: { + value?: { + [key: string]: string; + }; + }; + // (undocumented) + _links?: Links; +} + +// @public +export interface DaVinciPollResponse extends DaVinciBaseResponse { + // (undocumented) + eventName?: string; + // (undocumented) + _links?: Links; + // (undocumented) + success?: boolean; +} + +// @public (undocumented) +export interface DaVinciRequest { + // (undocumented) + eventName: string; + // (undocumented) + id: string; + // (undocumented) + interactionId: string; + // (undocumented) + parameters: { + eventType: 'submit' | 'action' | 'polling'; + data: { + actionKey: string; + formData?: Record; + }; + }; +} + +// @public +export type DaVinciRequestValueTypes = string | number | boolean | (string | number | boolean)[] | DeviceValue | PhoneNumberInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue | MetadataError | GenericError; + +// @public (undocumented) +export interface DaVinciSuccessResponse extends DaVinciBaseResponse { + // (undocumented) + authorizeResponse?: OAuthDetails; + // (undocumented) + environment: { + id: string; + [key: string]: unknown; + }; + // (undocumented) + _links?: Links; + // (undocumented) + resetCookie?: boolean; + // (undocumented) + session?: { + id?: string; + [key: string]: unknown; + }; + // (undocumented) + sessionToken?: string; + // (undocumented) + sessionTokenMaxAge?: number; + // (undocumented) + status: string; + // (undocumented) + subFlowSettings?: { + cssLinks?: unknown[]; + cssUrl?: unknown; + jsLinks?: unknown[]; + loadingScreenSettings?: unknown; + reactSkUrl?: unknown; + }; + // (undocumented) + success: true; +} + +// @public (undocumented) +export type DeviceAuthenticationCollector = ObjectOptionsCollectorWithObjectValue<'DeviceAuthenticationCollector', DeviceValue>; + +// @public (undocumented) +export type DeviceAuthenticationField = { + type: 'DEVICE_AUTHENTICATION'; + key: string; + label: string; + options: { + type: string; + iconSrc: string; + title: string; + id: string; + default: boolean; + description: string; + }[]; + required: boolean; +}; + +// @public (undocumented) +export interface DeviceOptionNoDefault { + // (undocumented) + content: string; + // (undocumented) + key: string; + // (undocumented) + label: string; + // (undocumented) + type: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export interface DeviceOptionWithDefault { + // (undocumented) + content: string; + // (undocumented) + default: boolean; + // (undocumented) + key: string; + // (undocumented) + label: string; + // (undocumented) + type: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export type DeviceRegistrationCollector = ObjectOptionsCollectorWithStringValue<'DeviceRegistrationCollector', string>; + +// @public (undocumented) +export type DeviceRegistrationField = { + type: 'DEVICE_REGISTRATION'; + key: string; + label: string; + options: { + type: string; + iconSrc: string; + title: string; + description: string; + }[]; + required: boolean; +}; + +// @public (undocumented) +export interface DeviceValue { + // (undocumented) + id: string; + // (undocumented) + type: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export interface ErrorDetail { + // (undocumented) + message?: string; + // (undocumented) + rawResponse?: { + _embedded?: { + users?: Array; + }; + code?: string; + count?: number; + details?: NestedErrorDetails[]; + id?: string; + message?: string; + size?: number; + userFilter?: string; + [key: string]: unknown; + }; + // (undocumented) + statusCode?: number; +} + +// @public (undocumented) +export interface ErrorNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: 'error'; + }; + // (undocumented) + error: DaVinciError; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: 'error'; + } | null; + // (undocumented) + status: 'error'; +} + +// @public (undocumented) +export interface FailureNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + status: 'failure'; + }; + // (undocumented) + error: DaVinciError; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + eventName?: string; + href?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: 'failure'; + } | null; + // (undocumented) + status: 'failure'; +} + +// @public +export function fido(): FidoClient; + +// @public (undocumented) +export type FidoAuthenticationCollector = AutoCollector<'ObjectValueAutoCollector', 'FidoAuthenticationCollector', FidoAuthenticationInputValue | GenericError, FidoAuthenticationOutputValue>; + +// @public (undocumented) +export type FidoAuthenticationField = { + type: 'FIDO2'; + key: string; + label: string; + publicKeyCredentialRequestOptions: FidoAuthenticationOptions; + action: 'AUTHENTICATE'; + trigger: string; + required: boolean; +}; + +// @public (undocumented) +export interface FidoAuthenticationInputValue { + // (undocumented) + assertionValue?: AssertionValue; +} + +// @public (undocumented) +export interface FidoAuthenticationOptions extends Omit { + // (undocumented) + allowCredentials?: { + id: number[]; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; + }[]; + // (undocumented) + challenge: number[]; +} + +// @public (undocumented) +export interface FidoAuthenticationOutputValue { + // (undocumented) + action: 'AUTHENTICATE'; + // (undocumented) + publicKeyCredentialRequestOptions: FidoAuthenticationOptions; + // (undocumented) + trigger: string; +} + +// @public (undocumented) +export interface FidoClient { + authenticate: (options: FidoAuthenticationOptions) => Promise; + register: (options: FidoRegistrationOptions) => Promise; +} + +// @public +export type FidoErrorCode = 'NotAllowedError' | 'AbortError' | 'InvalidStateError' | 'NotSupportedError' | 'SecurityError' | 'TimeoutError' | 'UnknownError'; + +// @public (undocumented) +export type FidoRegistrationCollector = AutoCollector<'ObjectValueAutoCollector', 'FidoRegistrationCollector', FidoRegistrationInputValue | GenericError, FidoRegistrationOutputValue>; + +// @public (undocumented) +export type FidoRegistrationField = { + type: 'FIDO2'; + key: string; + label: string; + publicKeyCredentialCreationOptions: FidoRegistrationOptions; + action: 'REGISTER'; + trigger: string; + required: boolean; +}; + +// @public (undocumented) +export interface FidoRegistrationInputValue { + // (undocumented) + attestationValue?: AttestationValue; +} + +// @public (undocumented) +export interface FidoRegistrationOptions extends Omit { + // (undocumented) + challenge: number[]; + // (undocumented) + excludeCredentials?: { + id: number[]; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; + }[]; + // (undocumented) + pubKeyCredParams: { + alg: string | number; + type: PublicKeyCredentialType; + }[]; + // (undocumented) + user: { + id: number[]; + name: string; + displayName: string; + }; +} + +// @public (undocumented) +export interface FidoRegistrationOutputValue { + // (undocumented) + action: 'REGISTER'; + // (undocumented) + publicKeyCredentialCreationOptions: FidoRegistrationOptions; + // (undocumented) + trigger: string; +} + +// @public (undocumented) +export type FlowCollector = ActionCollectorNoUrl<'FlowCollector'>; + +// @public (undocumented) +export type FlowNode = ContinueNode | ErrorNode | StartNode | SuccessNode | FailureNode; + +// @public +export type GetClient = StartNode['client'] | ContinueNode['client'] | ErrorNode['client'] | SuccessNode['client'] | FailureNode['client']; + +// @public (undocumented) +export type IdpCollector = ActionCollectorWithUrl<'IdpCollector'>; + +// @public +export interface ImageCollector extends NoValueCollectorBase<'ImageCollector'> { + // (undocumented) + output: NoValueCollectorBase<'ImageCollector'>['output'] & { + src: string; + alt: string; + href?: string; + }; +} + +// @public (undocumented) +export type ImageField = { + type: 'IMAGE'; + key: string; + description: string; + imageUrl: string; + hyperlinkUrl?: string; +}; + +// @public (undocumented) +export type InferActionCollectorType = T extends 'IdpCollector' ? IdpCollector : T extends 'SubmitCollector' ? SubmitCollector : T extends 'FlowCollector' ? FlowCollector : ActionCollectorWithUrl<'ActionCollector'> | ActionCollectorNoUrl<'ActionCollector'>; + +// @public +export type InferAutoCollectorType = T extends 'ProtectCollector' ? ProtectCollector : T extends 'PollingCollector' ? PollingCollector : T extends 'FidoRegistrationCollector' ? FidoRegistrationCollector : T extends 'FidoAuthenticationCollector' ? FidoAuthenticationCollector : T extends 'MetadataCollector' ? MetadataCollector : T extends 'ObjectValueAutoCollector' ? ObjectValueAutoCollector : SingleValueAutoCollector; + +// @public +export type InferMultiValueCollectorType = T extends 'MultiSelectCollector' ? MultiValueCollectorWithValue<'MultiSelectCollector'> : MultiValueCollectorWithValue<'MultiValueCollector'> | MultiValueCollectorNoValue<'MultiValueCollector'>; + +// @public +export type InferNoValueCollectorType = T extends 'ReadOnlyCollector' ? ReadOnlyCollector : T extends 'RichTextCollector' ? RichTextCollector : T extends 'QrCodeCollector' ? QrCodeCollector : T extends 'ImageCollector' ? ImageCollector : NoValueCollectorBase<'NoValueCollector'>; + +// @public +export type InferSingleValueCollectorType = T extends 'TextCollector' ? TextCollector : T extends 'SingleSelectCollector' ? SingleSelectCollector : T extends 'ValidatedTextCollector' ? ValidatedTextCollector : T extends 'PasswordCollector' ? PasswordCollector : T extends 'ValidatedPasswordCollector' ? ValidatedPasswordCollector : T extends 'BooleanCollector' ? BooleanCollector : T extends 'ValidatedBooleanCollector' ? ValidatedBooleanCollector : SingleValueCollectorWithValue<'SingleValueCollector'> | SingleValueCollectorNoValue<'SingleValueCollector'>; + +// @public (undocumented) +export type InferValueObjectCollectorType = T extends 'DeviceAuthenticationCollector' ? DeviceAuthenticationCollector : T extends 'DeviceRegistrationCollector' ? DeviceRegistrationCollector : T extends 'PhoneNumberCollector' ? PhoneNumberCollector : T extends 'PhoneNumberExtensionCollector' ? PhoneNumberExtensionCollector : ObjectOptionsCollectorWithObjectValue<'ObjectValueCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectValueCollector'>; + +// @public (undocumented) +export type InitFlow = () => Promise; + +// @public (undocumented) +export interface InternalErrorResponse { + // (undocumented) + error: Omit & { + message: string; + }; + // (undocumented) + type: 'internal_error'; +} + +// @public (undocumented) +export interface Links { + // (undocumented) + [key: string]: { + href?: string; + }; +} + +export { LogLevel } + +export { makeDavinciConfig } + +// @public (undocumented) +export type MetadataCollector = AutoCollector<'ObjectValueAutoCollector', 'MetadataCollector', MetadataCollectorInputValue, Record>; + +// @public (undocumented) +export type MetadataCollectorInputValue = Record | MetadataError; + +// @public +export interface MetadataError { + // (undocumented) + code: string; + // (undocumented) + message: string; +} + +// @public (undocumented) +export type MetadataField = { + type: 'METADATA'; + key: string; + payload: Record; +}; + +// @public (undocumented) +export type MultiSelectCollector = MultiValueCollectorWithValue<'MultiSelectCollector'>; + +// @public (undocumented) +export type MultiSelectField = { + inputType: 'MULTI_SELECT'; + key: string; + label: string; + options: { + label: string; + value: string; + }[]; + required?: boolean; + type: 'CHECKBOX' | 'COMBOBOX'; +}; + +// @public (undocumented) +export type MultiValueCollector = MultiValueCollectorWithValue | MultiValueCollectorNoValue; + +// @public (undocumented) +export interface MultiValueCollectorNoValue { + // (undocumented) + category: 'MultiValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string[]; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type MultiValueCollectors = MultiValueCollectorWithValue<'MultiValueCollector'> | MultiValueCollectorWithValue<'MultiSelectCollector'>; + +// @public +export type MultiValueCollectorTypes = 'MultiSelectCollector' | 'MultiValueCollector'; + +// @public (undocumented) +export interface MultiValueCollectorWithValue { + // (undocumented) + category: 'MultiValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string[]; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: string[]; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type MultiValueFields = MultiSelectField; + +// @public +export interface NestedErrorDetails { + // (undocumented) + code?: string; + // (undocumented) + innerError?: { + history?: string; + unsatisfiedRequirements?: string[]; + failuresRemaining?: number; + }; + // (undocumented) + message?: string; + // (undocumented) + target?: string; +} + +// @public +export const nextCollectorValues: ActionCreatorWithPayload< { +fields: DaVinciField[]; +formData: { +value: Record; +}; +}, string>; + +// @public +export const nodeCollectorReducer: Reducer & { + getInitialState: () => Collectors[]; +}; + +// @public (undocumented) +export type NodeStates = StartNode | ContinueNode | ErrorNode | SuccessNode | FailureNode; + +// @public (undocumented) +export type NoValueCollector = InferNoValueCollectorType; + +// @public (undocumented) +export interface NoValueCollectorBase { + // (undocumented) + category: 'NoValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type NoValueCollectors = NoValueCollectorBase<'NoValueCollector'> | ReadOnlyCollector | RichTextCollector | QrCodeCollector | ImageCollector; + +// @public +export type NoValueCollectorTypes = 'ReadOnlyCollector' | 'RichTextCollector' | 'NoValueCollector' | 'QrCodeCollector' | 'ImageCollector'; + +// @public +export interface OAuthDetails { + // (undocumented) + [key: string]: unknown; + // (undocumented) + code?: string; + // (undocumented) + state?: string; +} + +// @public (undocumented) +export interface ObjectOptionsCollectorWithObjectValue, D = Record> { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: V; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: DeviceOptionWithDefault[]; + value?: D | null; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export interface ObjectOptionsCollectorWithStringValue { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: V; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: DeviceOptionNoDefault[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type ObjectValueAutoCollector = AutoCollector<'ObjectValueAutoCollector', 'ObjectValueAutoCollector', Record>; + +// @public (undocumented) +export type ObjectValueAutoCollectorTypes = 'ObjectValueAutoCollector' | 'FidoRegistrationCollector' | 'FidoAuthenticationCollector' | 'MetadataCollector'; + +// @public (undocumented) +export type ObjectValueCollector = ObjectOptionsCollectorWithObjectValue | ObjectOptionsCollectorWithStringValue | ObjectValueCollectorWithObjectValue; + +// @public (undocumented) +export type ObjectValueCollectors = DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ObjectOptionsCollectorWithObjectValue<'ObjectSelectCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectSelectCollector'>; + +// @public +export type ObjectValueCollectorTypes = 'DeviceAuthenticationCollector' | 'DeviceRegistrationCollector' | 'PhoneNumberCollector' | 'PhoneNumberExtensionCollector' | 'ObjectOptionsCollector' | 'ObjectValueCollector' | 'ObjectSelectCollector'; + +// @public (undocumented) +export interface ObjectValueCollectorWithObjectValue, OV = Record> { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: IV; + type: string; + validation: (ValidationRequired | ValidationPhoneNumber)[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value?: OV | null; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export interface OutgoingQueryParams { + // (undocumented) + [key: string]: string | string[]; +} + +// @public (undocumented) +export interface PasswordCollector { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + verify: boolean; + }; + // (undocumented) + type: 'PasswordCollector'; +} + +// @public +export type PasswordField = { + type: 'PASSWORD' | 'PASSWORD_VERIFY'; + key: string; + label: string; + required?: boolean; + verify?: boolean; + passwordPolicy?: PasswordPolicy; +}; + +// @public +export interface PasswordPolicy { + // (undocumented) + createdAt?: string; + // (undocumented) + default?: boolean; + // (undocumented) + description?: string; + // (undocumented) + excludesCommonlyUsed?: boolean; + // (undocumented) + excludesProfileData?: boolean; + // (undocumented) + history?: { + count?: number; + retentionDays?: number; + }; + // (undocumented) + id?: string; + // (undocumented) + length?: { + min?: number; + max?: number; + }; + // (undocumented) + lockout?: { + failureCount?: number; + durationSeconds?: number; + }; + // (undocumented) + maxAgeDays?: number; + // (undocumented) + maxRepeatedCharacters?: number; + // (undocumented) + minAgeDays?: number; + // (undocumented) + minCharacters?: Record; + // (undocumented) + minUniqueCharacters?: number; + // (undocumented) + name?: string; + // (undocumented) + notSimilarToCurrent?: boolean; + // (undocumented) + populationCount?: number; + // (undocumented) + updatedAt?: string; +} + +// @public (undocumented) +export type PhoneNumberCollector = ObjectValueCollectorWithObjectValue<'PhoneNumberCollector', PhoneNumberInputValue, PhoneNumberOutputValue>; + +// @public (undocumented) +export interface PhoneNumberExtensionCollector { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: PhoneNumberExtensionInputValue; + type: string; + validation: (ValidationRequired | ValidationPhoneNumber)[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + extensionLabel: string; + value: PhoneNumberExtensionOutputValue; + }; + // (undocumented) + type: 'PhoneNumberExtensionCollector'; +} + +// @public (undocumented) +export type PhoneNumberExtensionField = PhoneNumberField & { + showExtension: boolean; + extensionLabel: string; +}; + +// @public (undocumented) +export interface PhoneNumberExtensionInputValue { + // (undocumented) + countryCode: string; + // (undocumented) + extension: string; + // (undocumented) + phoneNumber: string; +} + +// @public (undocumented) +export interface PhoneNumberExtensionOutputValue { + // (undocumented) + countryCode?: string; + // (undocumented) + extension?: string; + // (undocumented) + phoneNumber?: string; +} + +// @public (undocumented) +export type PhoneNumberField = { + type: 'PHONE_NUMBER'; + key: string; + label: string; + required: boolean; + defaultCountryCode: string | null; + validatePhoneNumber: boolean; +}; + +// @public (undocumented) +export interface PhoneNumberInputValue { + // (undocumented) + countryCode: string; + // (undocumented) + phoneNumber: string; +} + +// @public (undocumented) +export interface PhoneNumberOutputValue { + // (undocumented) + countryCode?: string; + // (undocumented) + phoneNumber?: string; +} + +// @public +export type Poller = () => Promise; + +// @public (undocumented) +export type PollingCollector = AutoCollector<'SingleValueAutoCollector', 'PollingCollector', string, PollingOutputValue>; + +// @public (undocumented) +export type PollingField = { + type: 'POLLING'; + key: string; + pollInterval: number; + pollRetries: number; + pollChallengeStatus?: boolean; + challenge?: string; +}; + +// @public (undocumented) +export interface PollingOutputValue { + // (undocumented) + challenge?: string; + // (undocumented) + pollChallengeStatus?: boolean; + // (undocumented) + pollInterval: number; + // (undocumented) + pollRetries: number; + // (undocumented) + retriesRemaining?: number; +} + +// @public (undocumented) +export type PollingStatus = PollingStatusContinue | PollingStatusChallenge; + +// @public (undocumented) +export type PollingStatusChallenge = PollingStatusChallengeComplete | 'expired' | 'timedOut' | 'error'; + +// @public (undocumented) +export type PollingStatusChallengeComplete = 'approved' | 'denied' | 'continue' | CustomPollingStatus; + +// @public (undocumented) +export type PollingStatusContinue = 'continue' | 'timedOut'; + +// @public (undocumented) +export type ProtectCollector = AutoCollector<'SingleValueAutoCollector', 'ProtectCollector', string, ProtectOutputValue>; + +// @public (undocumented) +export type ProtectField = { + type: 'PROTECT'; + key: string; + behavioralDataCollection: boolean; + universalDeviceIdentification: boolean; +}; + +// @public +export interface ProtectOutputValue { + // (undocumented) + behavioralDataCollection: boolean; + // (undocumented) + universalDeviceIdentification: boolean; +} + +// @public +export interface QrCodeCollector extends NoValueCollectorBase<'QrCodeCollector'> { + // (undocumented) + output: NoValueCollectorBase<'QrCodeCollector'>['output'] & { + src: string; + }; +} + +// @public (undocumented) +export type QrCodeField = { + type: 'QR_CODE'; + key: string; + content: string; + fallbackText?: string; +}; + +// @public +export interface ReadOnlyCollector extends NoValueCollectorBase<'ReadOnlyCollector'> { + // (undocumented) + output: NoValueCollectorBase<'ReadOnlyCollector'>['output'] & { + content: string; + title?: string; + }; +} + +// @public (undocumented) +export type ReadOnlyField = { + type: 'LABEL'; + content: string; + richContent?: RichContent; + key?: string; +}; + +// @public (undocumented) +export type ReadOnlyFields = ReadOnlyField | QrCodeField | AgreementField | ImageField; + +// @public (undocumented) +export type RedirectField = { + type: 'SOCIAL_LOGIN_BUTTON'; + key: string; + label: string; + links: Links; +}; + +// @public (undocumented) +export type RedirectFields = RedirectField; + +export { RequestMiddleware } + +// @public +export type RichContent = { + content: string; + replacements?: Record; +}; + +// @public +export interface RichContentLink { + // (undocumented) + href: string; + // (undocumented) + key: string; + // (undocumented) + target?: '_self' | '_blank'; + // (undocumented) + type: 'link'; + // (undocumented) + value: string; +} + +// @public +export type RichContentReplacement = { + type: 'link'; + value: string; + href: string; + target?: '_self' | '_blank'; +}; + +// @public +export interface RichTextCollector extends NoValueCollectorBase<'RichTextCollector'> { + // (undocumented) + output: NoValueCollectorBase<'RichTextCollector'>['output'] & { + content: string; + richContent: CollectorRichContent; + }; +} + +// @public (undocumented) +export interface SelectorOption { + // (undocumented) + label: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export type SingleCheckboxField = { + type: 'SINGLE_CHECKBOX'; + inputType: 'BOOLEAN'; + key: string; + label: string; + required?: boolean; + errorMessage?: string; + appearance: string; + richContent?: RichContent; +}; + +// @public (undocumented) +export type SingleSelectCollector = SingleSelectCollectorWithValue<'SingleSelectCollector'>; + +// @public (undocumented) +export interface SingleSelectCollectorNoValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export interface SingleSelectCollectorWithValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: string | number | boolean; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type SingleSelectField = { + inputType: 'SINGLE_SELECT'; + key: string; + label: string; + options: { + label: string; + value: string; + }[]; + required?: boolean; + type: 'RADIO' | 'DROPDOWN'; +}; + +// @public (undocumented) +export type SingleValueAutoCollector = AutoCollector<'SingleValueAutoCollector', 'SingleValueAutoCollector', string>; + +// @public (undocumented) +export type SingleValueAutoCollectorTypes = 'SingleValueAutoCollector' | 'ProtectCollector' | 'PollingCollector'; + +// @public +export type SingleValueCollector = SingleValueCollectorWithValue | SingleValueCollectorNoValue; + +// @public (undocumented) +export interface SingleValueCollectorNoValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type SingleValueCollectors = PasswordCollector | ValidatedPasswordCollector | SingleSelectCollector | TextCollector | ValidatedTextCollector | BooleanCollector | ValidatedBooleanCollector | SingleValueCollectorWithValue<'SingleValueCollector'>; + +// @public +export type SingleValueCollectorTypes = 'PasswordCollector' | 'ValidatedPasswordCollector' | 'BooleanCollector' | 'ValidatedBooleanCollector' | 'SingleValueCollector' | 'SingleSelectCollector' | 'SingleSelectObjectCollector' | 'TextCollector' | 'ValidatedTextCollector'; + +// @public (undocumented) +export interface SingleValueCollectorWithValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: V; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: V; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type SingleValueFields = StandardField | PasswordField | ValidatedField | SingleCheckboxField | SingleSelectField | ProtectField; + +// @public (undocumented) +export type StandardField = { + type: 'TEXT' | 'SUBMIT_BUTTON' | 'FLOW_BUTTON' | 'FLOW_LINK' | 'BUTTON'; + key: string; + label: string; + required?: boolean; +}; + +// @public (undocumented) +export interface StartNode { + // (undocumented) + cache: null; + // (undocumented) + client: { + status: 'start'; + }; + // (undocumented) + error: DaVinciError | null; + // (undocumented) + server: { + status: 'start'; + }; + // (undocumented) + status: 'start'; +} + +// @public (undocumented) +export interface StartOptions { + // (undocumented) + query: Query; +} + +// @public (undocumented) +export type SubmitCollector = ActionCollectorNoUrl<'SubmitCollector'>; + +// @public (undocumented) +export interface SuccessNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + authorization?: { + code?: string; + state?: string; + }; + status: 'success'; + } | null; + // (undocumented) + error: null; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + session?: string; + status: 'success'; + }; + // (undocumented) + status: 'success'; +} + +// @public (undocumented) +export type TextCollector = SingleValueCollectorWithValue<'TextCollector'>; + +// @public (undocumented) +export interface ThrownQueryError { + // (undocumented) + error: FetchBaseQueryError; + // (undocumented) + isHandledError: boolean; + // (undocumented) + meta: FetchBaseQueryMeta; +} + +// @public +export type UnknownCollector = { + category: 'UnknownCollector'; + error: string | null; + type: 'UnknownCollector'; + id: string; + name: string; + output: { + key: string; + label: string; + type: string; + }; +}; + +// @public (undocumented) +export type UnknownField = Record; + +// @public +export type UpdatableCollectors = SingleValueCollectors | MultiSelectCollector | ObjectValueCollectors | AutoCollectors; + +// @public (undocumented) +export const updateCollectorValues: ActionCreatorWithPayload< { +id: string; +value: CollectorValueTypes; +index?: number; +}, string>; + +// @public +export type Updater = (value: CollectorValueType, index?: number) => InternalErrorResponse | null; + +// @public (undocumented) +export interface ValidatedBooleanCollector extends ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean> { + // (undocumented) + output: ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean>['output'] & { + appearance: string; + richContent?: CollectorRichContent; + }; +} + +// @public +export type ValidatedCollectors = ValidatedTextCollector | ValidatedBooleanCollector | ValidatedPasswordCollector | ObjectValueCollectors | MultiValueCollectors | AutoCollectors; + +// @public (undocumented) +export type ValidatedField = { + type: 'TEXT'; + key: string; + label: string; + required: boolean; + validation: { + regex: string; + errorMessage: string; + }; +}; + +// @public (undocumented) +export interface ValidatedPasswordCollector { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + validation: PasswordPolicy; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + verify: boolean; + }; + // (undocumented) + type: 'ValidatedPasswordCollector'; +} + +// @public (undocumented) +export interface ValidatedSingleValueCollectorWithValue { + // (undocumented) + category: 'ValidatedSingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + type: string; + validation: (ValidationRequired | ValidationRegex)[]; + value: V; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: V; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type ValidatedTextCollector = ValidatedSingleValueCollectorWithValue<'TextCollector'>; + +// @public (undocumented) +export interface ValidationPhoneNumber { + // (undocumented) + message: string; + // (undocumented) + rule: boolean; + // (undocumented) + type: 'validatePhoneNumber'; +} + +// @public (undocumented) +export interface ValidationRegex { + // (undocumented) + message: string; + // (undocumented) + rule: string; + // (undocumented) + type: 'regex'; +} + +// @public (undocumented) +export interface ValidationRequired { + // (undocumented) + message: string; + // (undocumented) + rule: boolean; + // (undocumented) + type: 'required'; +} + +// @public +export type Validator = (value: CollectorValueType) => string[] | { + error: { + message: string; + type: string; + }; + type: string; +}; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/davinci-client/api-report/davinci-client.types.api.md b/packages/davinci-client/api-report/davinci-client.types.api.md index cfece3b8487..8d3a7724010 100644 --- a/packages/davinci-client/api-report/davinci-client.types.api.md +++ b/packages/davinci-client/api-report/davinci-client.types.api.md @@ -1,2437 +1,2019 @@ -## API Report File for "@forgerock/davinci-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import type { DaVinciConfig } from '@forgerock/sdk-types'; -import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; -import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; -import type { GenericError } from '@forgerock/sdk-types'; -import { LogLevel } from '@forgerock/sdk-logger'; -import type { MutationResultSelectorResult } from '@reduxjs/toolkit/query'; -import { QueryStatus } from '@reduxjs/toolkit/query'; -import { Reducer } from '@reduxjs/toolkit'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { SerializedError } from '@reduxjs/toolkit'; -import { Unsubscribe } from '@reduxjs/toolkit'; - -// @public (undocumented) -export type ActionCollector = - | ActionCollectorNoUrl - | ActionCollectorWithUrl; - -// @public (undocumented) -export interface ActionCollectorNoUrl { - // (undocumented) - category: 'ActionCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type ActionCollectors = - | ActionCollectorWithUrl<'IdpCollector'> - | ActionCollectorNoUrl<'ActionCollector'> - | ActionCollectorNoUrl<'FlowCollector'> - | ActionCollectorNoUrl<'SubmitCollector'>; - -// @public -export type ActionCollectorTypes = - | 'FlowCollector' - | 'SubmitCollector' - | 'IdpCollector' - | 'ActionCollector'; - -// @public (undocumented) -export interface ActionCollectorWithUrl { - // (undocumented) - category: 'ActionCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - url?: string | null; - }; - // (undocumented) - type: T; -} - -export { ActionTypes }; - -// @public (undocumented) -export type AgreementField = { - type: 'AGREEMENT'; - key: string; - content: string; - titleEnabled: boolean; - title?: string; - agreement: { - id: string; - useDynamicAgreement: boolean; - }; - enabled: boolean; -}; - -// @public (undocumented) -export interface AssertionValue extends Omit< - PublicKeyCredential, - 'rawId' | 'response' | 'getClientExtensionResults' | 'toJSON' -> { - // (undocumented) - rawId: string; - // (undocumented) - response: { - clientDataJSON: string; - authenticatorData: string; - signature: string; - userHandle: string | null; - }; -} - -// @public (undocumented) -export interface AttestationValue extends Omit< - PublicKeyCredential, - 'rawId' | 'response' | 'getClientExtensionResults' | 'toJSON' -> { - // (undocumented) - rawId: string; - // (undocumented) - response: { - clientDataJSON: string; - attestationObject: string; - }; -} - -// @public (undocumented) -export interface AutoCollector< - C extends AutoCollectorCategories, - T extends AutoCollectorTypes, - IV = string, - OV = Record, -> { - // (undocumented) - category: C; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: IV; - type: string; - validation?: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - type: string; - config: OV; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type AutoCollectorCategories = 'SingleValueAutoCollector' | 'ObjectValueAutoCollector'; - -// @public (undocumented) -export type AutoCollectors = - | ProtectCollector - | FidoRegistrationCollector - | FidoAuthenticationCollector - | MetadataCollector - | PollingCollector - | SingleValueAutoCollector - | ObjectValueAutoCollector; - -// @public (undocumented) -export type AutoCollectorTypes = SingleValueAutoCollectorTypes | ObjectValueAutoCollectorTypes; - -// @public (undocumented) -export interface BooleanCollector extends SingleValueCollectorWithValue< - 'BooleanCollector', - boolean -> { - // (undocumented) - output: SingleValueCollectorWithValue<'BooleanCollector', boolean>['output'] & { - appearance: string; - richContent?: CollectorRichContent; - }; -} - -// @public (undocumented) -export type CollectorCategory = Collectors['category']; - -// @public (undocumented) -export interface CollectorErrors { - // (undocumented) - code: string; - // (undocumented) - message: string; - // (undocumented) - target: string; -} - -// @public -export interface CollectorRichContent { - // (undocumented) - content: string; - // (undocumented) - replacements: RichContentLink[]; -} - -// @public (undocumented) -export type Collectors = - | FlowCollector - | MetadataCollector - | PasswordCollector - | ValidatedPasswordCollector - | TextCollector - | BooleanCollector - | ValidatedBooleanCollector - | SingleSelectCollector - | IdpCollector - | SubmitCollector - | ActionCollector<'ActionCollector'> - | SingleValueCollector<'SingleValueCollector'> - | MultiSelectCollector - | DeviceAuthenticationCollector - | DeviceRegistrationCollector - | PhoneNumberCollector - | PhoneNumberExtensionCollector - | ReadOnlyCollector - | RichTextCollector - | ValidatedTextCollector - | ProtectCollector - | PollingCollector - | FidoRegistrationCollector - | FidoAuthenticationCollector - | QrCodeCollector - | ImageCollector - | UnknownCollector; - -// @public -export type CollectorValueType = T extends - | { - type: 'PasswordCollector'; - } - | { - type: 'ValidatedPasswordCollector'; - } - | { - type: 'SingleSelectCollector'; - } - | { - type: 'DeviceRegistrationCollector'; - } - | { - type: 'DeviceAuthenticationCollector'; - } - | { - type: 'ProtectCollector'; - } - | { - type: 'PollingCollector'; - } - ? string - : T extends { - type: 'TextCollector'; - category: 'SingleValueCollector'; - } - ? string - : T extends { - type: 'TextCollector'; - category: 'ValidatedSingleValueCollector'; - } - ? string - : T extends - | { - type: 'BooleanCollector'; - } - | { - type: 'ValidatedBooleanCollector'; - } - ? boolean - : T extends { - type: 'MultiSelectCollector'; - } - ? string | string[] - : T extends { - type: 'PhoneNumberCollector'; - } - ? PhoneNumberInputValue - : T extends { - type: 'PhoneNumberExtensionCollector'; - } - ? PhoneNumberExtensionInputValue - : T extends { - type: 'FidoRegistrationCollector'; - } - ? FidoRegistrationInputValue | GenericError - : T extends { - type: 'FidoAuthenticationCollector'; - } - ? FidoAuthenticationInputValue | GenericError - : T extends { - type: 'MetadataCollector'; - } - ? Record | MetadataError - : T extends { - category: 'SingleValueCollector'; - } - ? string - : T extends { - category: 'ValidatedSingleValueCollector'; - } - ? string - : T extends { - category: 'SingleValueAutoCollector'; - } - ? string - : T extends { - category: 'MultiValueCollector'; - } - ? string | string[] - : T extends { - category: 'ActionCollector'; - } - ? never - : T extends { - category: 'NoValueCollector'; - } - ? never - : CollectorValueTypes; - -// @public -export type CollectorValueTypes = - | string - | string[] - | boolean - | PhoneNumberInputValue - | PhoneNumberExtensionInputValue - | FidoRegistrationInputValue - | FidoAuthenticationInputValue - | MetadataError - | GenericError; - -// @public (undocumented) -export type ComplexValueFields = - | DeviceAuthenticationField - | DeviceRegistrationField - | PhoneNumberField - | PhoneNumberExtensionField - | FidoRegistrationField - | FidoAuthenticationField - | PollingField - | MetadataField; - -// @public (undocumented) -export interface ContinueNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'continue'; - }; - // (undocumented) - error: null; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - eventName?: string; - status: 'continue'; - }; - // (undocumented) - status: 'continue'; -} - -export { CustomLogger }; - -// @public -export type CustomPollingStatus = string & {}; - -// @public -export function davinci(input: { - config: DaVinciConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; -}): Promise<{ - subscribe: (listener: () => void) => Unsubscribe; - externalIdp: () => () => Promise; - flow: (action: DaVinciAction) => InitFlow; - next: (args?: DaVinciRequest) => Promise; - resume: (input: { continueToken: string }) => Promise; - start: ( - options?: StartOptions | undefined, - ) => Promise; - update: (collector: T) => Updater; - validate: ( - collector: - | SingleValueCollectors - | ObjectValueCollectors - | MultiValueCollectors - | AutoCollectors, - ) => Validator; - pollStatus: (collector: PollingCollector) => Poller; - getClient: () => - | { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'continue'; - } - | { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'error'; - } - | { - status: 'failure'; - } - | { - status: 'start'; - } - | { - authorization?: { - code?: string; - state?: string; - }; - status: 'success'; - } - | null; - getCollectors: () => Collectors[]; - getError: () => DaVinciError | null; - getErrorCollectors: () => CollectorErrors[]; - getNode: () => ContinueNode | ErrorNode | FailureNode | StartNode | SuccessNode; - getServer: () => - | { - _links?: Links; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - eventName?: string; - status: 'continue'; - } - | { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'error'; - } - | { - _links?: Links; - eventName?: string; - href?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'failure'; - } - | { - status: 'start'; - } - | { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - session?: string; - status: 'success'; - } - | null; - cache: { - getLatestResponse: () => - | ({ - status: QueryStatus.fulfilled; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > - > & { - error: undefined; - } & { - status: QueryStatus.fulfilled; - isUninitialized: false; - isLoading: false; - isSuccess: true; - isError: false; - }) - | ({ - status: QueryStatus.rejected; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > - > & { - status: QueryStatus.rejected; - isUninitialized: false; - isLoading: false; - isSuccess: false; - isError: true; - }) - | { - error: { - message: string; - type: string; - }; - }; - getResponseWithId: (requestId: string) => - | ({ - status: QueryStatus.fulfilled; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'data' | 'fulfilledTimeStamp' - > - > & { - error: undefined; - } & { - status: QueryStatus.fulfilled; - isUninitialized: false; - isLoading: false; - isSuccess: true; - isError: false; - }) - | ({ - status: QueryStatus.rejected; - } & Omit< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > & - Required< - Pick< - { - requestId: string; - data?: unknown; - error?: FetchBaseQueryError | SerializedError | undefined; - endpointName: string; - startedTimeStamp: number; - fulfilledTimeStamp?: number; - }, - 'error' - > - > & { - status: QueryStatus.rejected; - isUninitialized: false; - isLoading: false; - isSuccess: false; - isError: true; - }) - | { - error: { - message: string; - type: string; - }; - }; - }; -}>; - -// @public -export interface DaVinciAction { - // (undocumented) - action: string; -} - -// @public -export interface DaVinciBaseResponse { - // (undocumented) - capabilityName?: string; - // (undocumented) - companyId?: string; - // (undocumented) - connectionId?: string; - // (undocumented) - connectorId?: string; - // (undocumented) - id?: string; - // (undocumented) - interactionId?: string; - // (undocumented) - interactionToken?: string; - // (undocumented) - isResponseCompatibleWithMobileAndWebSdks?: boolean; - // (undocumented) - status?: string; -} - -// @public -export type DaVinciCacheEntry = { - data?: DaVinciBaseResponse; - error?: { - data: DaVinciBaseResponse; - status: number; - }; -} & { - data?: any; - error?: any; -} & MutationResultSelectorResult; - -// @public (undocumented) -export type DavinciClient = Awaited>; - -export { DaVinciConfig }; - -// @public (undocumented) -export interface DaVinciError extends Omit { - // (undocumented) - collectors?: CollectorErrors[]; - // (undocumented) - internalHttpStatus?: number; - // (undocumented) - message: string; - // (undocumented) - status: 'error' | 'failure' | 'unknown'; -} - -// @public (undocumented) -export interface DaVinciErrorCacheEntry { - // (undocumented) - endpointName: 'next' | 'flow' | 'start'; - // (undocumented) - error: { - data: T; - }; - // (undocumented) - fulfilledTimeStamp: number; - // (undocumented) - isError: boolean; - // (undocumented) - isLoading: boolean; - // (undocumented) - isSuccess: boolean; - // (undocumented) - isUninitialized: boolean; - // (undocumented) - requestId: string; - // (undocumented) - startedTimeStamp: number; - // (undocumented) - status: 'fulfilled' | 'pending' | 'rejected'; -} - -// @public (undocumented) -export interface DavinciErrorResponse extends DaVinciBaseResponse { - // (undocumented) - cause?: string | null; - // (undocumented) - code: string | number; - // (undocumented) - details?: ErrorDetail[]; - // (undocumented) - doNotSendToOE?: boolean; - // (undocumented) - error?: { - code?: string; - message?: string; - }; - // (undocumented) - errorCategory?: string; - // (undocumented) - errorMessage?: string; - // (undocumented) - expected?: boolean; - // (undocumented) - httpResponseCode: number; - // (undocumented) - isErrorCustomized?: boolean; - // (undocumented) - message: string; - // (undocumented) - metricAttributes?: { - [key: string]: unknown; - }; -} - -// @public (undocumented) -export interface DaVinciFailureResponse extends DaVinciBaseResponse { - // (undocumented) - error?: { - code?: string; - message?: string; - [key: string]: unknown; - }; -} - -// @public (undocumented) -export type DaVinciField = - | ComplexValueFields - | MultiValueFields - | ReadOnlyFields - | RedirectFields - | SingleValueFields; - -// @public -export interface DaVinciNextResponse extends DaVinciBaseResponse { - // (undocumented) - eventName?: string; - // (undocumented) - form?: { - name?: string; - description?: string; - components?: { - fields?: DaVinciField[]; - }; - }; - // (undocumented) - formData?: { - value?: { - [key: string]: string; - }; - }; - // (undocumented) - _links?: Links; -} - -// @public -export interface DaVinciPollResponse extends DaVinciBaseResponse { - // (undocumented) - eventName?: string; - // (undocumented) - _links?: Links; - // (undocumented) - success?: boolean; -} - -// @public (undocumented) -export interface DaVinciRequest { - // (undocumented) - eventName: string; - // (undocumented) - id: string; - // (undocumented) - interactionId: string; - // (undocumented) - parameters: { - eventType: 'submit' | 'action' | 'polling'; - data: { - actionKey: string; - formData?: Record; - }; - }; -} - -// @public -export type DaVinciRequestValueTypes = - | string - | number - | boolean - | (string | number | boolean)[] - | DeviceValue - | PhoneNumberInputValue - | FidoRegistrationInputValue - | FidoAuthenticationInputValue - | MetadataError - | GenericError; - -// @public (undocumented) -export interface DaVinciSuccessResponse extends DaVinciBaseResponse { - // (undocumented) - authorizeResponse?: OAuthDetails; - // (undocumented) - environment: { - id: string; - [key: string]: unknown; - }; - // (undocumented) - _links?: Links; - // (undocumented) - resetCookie?: boolean; - // (undocumented) - session?: { - id?: string; - [key: string]: unknown; - }; - // (undocumented) - sessionToken?: string; - // (undocumented) - sessionTokenMaxAge?: number; - // (undocumented) - status: string; - // (undocumented) - subFlowSettings?: { - cssLinks?: unknown[]; - cssUrl?: unknown; - jsLinks?: unknown[]; - loadingScreenSettings?: unknown; - reactSkUrl?: unknown; - }; - // (undocumented) - success: true; -} - -// @public (undocumented) -export type DeviceAuthenticationCollector = ObjectOptionsCollectorWithObjectValue< - 'DeviceAuthenticationCollector', - DeviceValue ->; - -// @public (undocumented) -export type DeviceAuthenticationField = { - type: 'DEVICE_AUTHENTICATION'; - key: string; - label: string; - options: { - type: string; - iconSrc: string; - title: string; - id: string; - default: boolean; - description: string; - }[]; - required: boolean; -}; - -// @public (undocumented) -export interface DeviceOptionNoDefault { - // (undocumented) - content: string; - // (undocumented) - key: string; - // (undocumented) - label: string; - // (undocumented) - type: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export interface DeviceOptionWithDefault { - // (undocumented) - content: string; - // (undocumented) - default: boolean; - // (undocumented) - key: string; - // (undocumented) - label: string; - // (undocumented) - type: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export type DeviceRegistrationCollector = ObjectOptionsCollectorWithStringValue< - 'DeviceRegistrationCollector', - string ->; - -// @public (undocumented) -export type DeviceRegistrationField = { - type: 'DEVICE_REGISTRATION'; - key: string; - label: string; - options: { - type: string; - iconSrc: string; - title: string; - description: string; - }[]; - required: boolean; -}; - -// @public (undocumented) -export interface DeviceValue { - // (undocumented) - id: string; - // (undocumented) - type: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export interface ErrorDetail { - // (undocumented) - message?: string; - // (undocumented) - rawResponse?: { - _embedded?: { - users?: Array; - }; - code?: string; - count?: number; - details?: NestedErrorDetails[]; - id?: string; - message?: string; - size?: number; - userFilter?: string; - [key: string]: unknown; - }; - // (undocumented) - statusCode?: number; -} - -// @public (undocumented) -export interface ErrorNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - action: string; - collectors: Collectors[]; - description?: string; - name?: string; - status: 'error'; - }; - // (undocumented) - error: DaVinciError; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'error'; - } | null; - // (undocumented) - status: 'error'; -} - -// @public (undocumented) -export interface FailureNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - status: 'failure'; - }; - // (undocumented) - error: DaVinciError; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - eventName?: string; - href?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - status: 'failure'; - } | null; - // (undocumented) - status: 'failure'; -} - -// @public (undocumented) -export type FidoAuthenticationCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'FidoAuthenticationCollector', - FidoAuthenticationInputValue | GenericError, - FidoAuthenticationOutputValue ->; - -// @public (undocumented) -export type FidoAuthenticationField = { - type: 'FIDO2'; - key: string; - label: string; - publicKeyCredentialRequestOptions: FidoAuthenticationOptions; - action: 'AUTHENTICATE'; - trigger: string; - required: boolean; -}; - -// @public (undocumented) -export interface FidoAuthenticationInputValue { - // (undocumented) - assertionValue?: AssertionValue; -} - -// @public (undocumented) -export interface FidoAuthenticationOptions extends Omit< - PublicKeyCredentialRequestOptions, - 'challenge' | 'allowCredentials' -> { - // (undocumented) - allowCredentials?: { - id: number[]; - transports?: AuthenticatorTransport[]; - type: PublicKeyCredentialType; - }[]; - // (undocumented) - challenge: number[]; -} - -// @public (undocumented) -export interface FidoAuthenticationOutputValue { - // (undocumented) - action: 'AUTHENTICATE'; - // (undocumented) - publicKeyCredentialRequestOptions: FidoAuthenticationOptions; - // (undocumented) - trigger: string; -} - -// @public (undocumented) -export interface FidoClient { - authenticate: ( - options: FidoAuthenticationOptions, - ) => Promise; - register: ( - options: FidoRegistrationOptions, - ) => Promise; -} - -// @public -export type FidoErrorCode = - | 'NotAllowedError' - | 'AbortError' - | 'InvalidStateError' - | 'NotSupportedError' - | 'SecurityError' - | 'TimeoutError' - | 'UnknownError'; - -// @public (undocumented) -export type FidoRegistrationCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'FidoRegistrationCollector', - FidoRegistrationInputValue | GenericError, - FidoRegistrationOutputValue ->; - -// @public (undocumented) -export type FidoRegistrationField = { - type: 'FIDO2'; - key: string; - label: string; - publicKeyCredentialCreationOptions: FidoRegistrationOptions; - action: 'REGISTER'; - trigger: string; - required: boolean; -}; - -// @public (undocumented) -export interface FidoRegistrationInputValue { - // (undocumented) - attestationValue?: AttestationValue; -} - -// @public (undocumented) -export interface FidoRegistrationOptions extends Omit< - PublicKeyCredentialCreationOptions, - 'challenge' | 'user' | 'pubKeyCredParams' | 'excludeCredentials' -> { - // (undocumented) - challenge: number[]; - // (undocumented) - excludeCredentials?: { - id: number[]; - transports?: AuthenticatorTransport[]; - type: PublicKeyCredentialType; - }[]; - // (undocumented) - pubKeyCredParams: { - alg: string | number; - type: PublicKeyCredentialType; - }[]; - // (undocumented) - user: { - id: number[]; - name: string; - displayName: string; - }; -} - -// @public (undocumented) -export interface FidoRegistrationOutputValue { - // (undocumented) - action: 'REGISTER'; - // (undocumented) - publicKeyCredentialCreationOptions: FidoRegistrationOptions; - // (undocumented) - trigger: string; -} - -// @public (undocumented) -export type FlowCollector = ActionCollectorNoUrl<'FlowCollector'>; - -// @public (undocumented) -export type FlowNode = ContinueNode | ErrorNode | StartNode | SuccessNode | FailureNode; - -// @public -export type GetClient = - | StartNode['client'] - | ContinueNode['client'] - | ErrorNode['client'] - | SuccessNode['client'] - | FailureNode['client']; - -// @public (undocumented) -export type IdpCollector = ActionCollectorWithUrl<'IdpCollector'>; - -// @public -export interface ImageCollector extends NoValueCollectorBase<'ImageCollector'> { - // (undocumented) - output: NoValueCollectorBase<'ImageCollector'>['output'] & { - src: string; - alt: string; - href?: string; - }; -} - -// @public (undocumented) -export type ImageField = { - type: 'IMAGE'; - key: string; - description: string; - imageUrl: string; - hyperlinkUrl?: string; -}; - -// @public (undocumented) -export type InferActionCollectorType = T extends 'IdpCollector' - ? IdpCollector - : T extends 'SubmitCollector' - ? SubmitCollector - : T extends 'FlowCollector' - ? FlowCollector - : ActionCollectorWithUrl<'ActionCollector'> | ActionCollectorNoUrl<'ActionCollector'>; - -// @public -export type InferAutoCollectorType = T extends 'ProtectCollector' - ? ProtectCollector - : T extends 'PollingCollector' - ? PollingCollector - : T extends 'FidoRegistrationCollector' - ? FidoRegistrationCollector - : T extends 'FidoAuthenticationCollector' - ? FidoAuthenticationCollector - : T extends 'MetadataCollector' - ? MetadataCollector - : T extends 'ObjectValueAutoCollector' - ? ObjectValueAutoCollector - : SingleValueAutoCollector; - -// @public -export type InferMultiValueCollectorType = - T extends 'MultiSelectCollector' - ? MultiValueCollectorWithValue<'MultiSelectCollector'> - : - | MultiValueCollectorWithValue<'MultiValueCollector'> - | MultiValueCollectorNoValue<'MultiValueCollector'>; - -// @public -export type InferNoValueCollectorType = - T extends 'ReadOnlyCollector' - ? ReadOnlyCollector - : T extends 'RichTextCollector' - ? RichTextCollector - : T extends 'QrCodeCollector' - ? QrCodeCollector - : T extends 'ImageCollector' - ? ImageCollector - : NoValueCollectorBase<'NoValueCollector'>; - -// @public -export type InferSingleValueCollectorType = - T extends 'TextCollector' - ? TextCollector - : T extends 'SingleSelectCollector' - ? SingleSelectCollector - : T extends 'ValidatedTextCollector' - ? ValidatedTextCollector - : T extends 'PasswordCollector' - ? PasswordCollector - : T extends 'ValidatedPasswordCollector' - ? ValidatedPasswordCollector - : T extends 'BooleanCollector' - ? BooleanCollector - : T extends 'ValidatedBooleanCollector' - ? ValidatedBooleanCollector - : - | SingleValueCollectorWithValue<'SingleValueCollector'> - | SingleValueCollectorNoValue<'SingleValueCollector'>; - -// @public (undocumented) -export type InferValueObjectCollectorType = - T extends 'DeviceAuthenticationCollector' - ? DeviceAuthenticationCollector - : T extends 'DeviceRegistrationCollector' - ? DeviceRegistrationCollector - : T extends 'PhoneNumberCollector' - ? PhoneNumberCollector - : T extends 'PhoneNumberExtensionCollector' - ? PhoneNumberExtensionCollector - : - | ObjectOptionsCollectorWithObjectValue<'ObjectValueCollector'> - | ObjectOptionsCollectorWithStringValue<'ObjectValueCollector'>; - -// @public (undocumented) -export type InitFlow = () => Promise; - -// @public (undocumented) -export interface InternalErrorResponse { - // (undocumented) - error: Omit & { - message: string; - }; - // (undocumented) - type: 'internal_error'; -} - -// @public (undocumented) -export interface Links { - // (undocumented) - [key: string]: { - href?: string; - }; -} - -export { LogLevel }; - -// @public (undocumented) -export type MetadataCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'MetadataCollector', - MetadataCollectorInputValue, - Record ->; - -// @public (undocumented) -export type MetadataCollectorInputValue = Record | MetadataError; - -// @public -export interface MetadataError { - // (undocumented) - code: string; - // (undocumented) - message: string; -} - -// @public (undocumented) -export type MetadataField = { - type: 'METADATA'; - key: string; - payload: Record; -}; - -// @public (undocumented) -export type MultiSelectCollector = MultiValueCollectorWithValue<'MultiSelectCollector'>; - -// @public (undocumented) -export type MultiSelectField = { - inputType: 'MULTI_SELECT'; - key: string; - label: string; - options: { - label: string; - value: string; - }[]; - required?: boolean; - type: 'CHECKBOX' | 'COMBOBOX'; -}; - -// @public (undocumented) -export type MultiValueCollector = - | MultiValueCollectorWithValue - | MultiValueCollectorNoValue; - -// @public (undocumented) -export interface MultiValueCollectorNoValue { - // (undocumented) - category: 'MultiValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string[]; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type MultiValueCollectors = - | MultiValueCollectorWithValue<'MultiValueCollector'> - | MultiValueCollectorWithValue<'MultiSelectCollector'>; - -// @public -export type MultiValueCollectorTypes = 'MultiSelectCollector' | 'MultiValueCollector'; - -// @public (undocumented) -export interface MultiValueCollectorWithValue { - // (undocumented) - category: 'MultiValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string[]; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: string[]; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type MultiValueFields = MultiSelectField; - -// @public -export interface NestedErrorDetails { - // (undocumented) - code?: string; - // (undocumented) - innerError?: { - history?: string; - unsatisfiedRequirements?: string[]; - failuresRemaining?: number; - }; - // (undocumented) - message?: string; - // (undocumented) - target?: string; -} - -// @public -export const nextCollectorValues: ActionCreatorWithPayload< - { - fields: DaVinciField[]; - formData: { - value: Record; - }; - }, - string ->; - -// @public -export const nodeCollectorReducer: Reducer & { - getInitialState: () => Collectors[]; -}; - -// @public (undocumented) -export type NodeStates = StartNode | ContinueNode | ErrorNode | SuccessNode | FailureNode; - -// @public (undocumented) -export type NoValueCollector = InferNoValueCollectorType; - -// @public (undocumented) -export interface NoValueCollectorBase { - // (undocumented) - category: 'NoValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type NoValueCollectors = - | NoValueCollectorBase<'NoValueCollector'> - | ReadOnlyCollector - | RichTextCollector - | QrCodeCollector - | ImageCollector; - -// @public -export type NoValueCollectorTypes = - | 'ReadOnlyCollector' - | 'RichTextCollector' - | 'NoValueCollector' - | 'QrCodeCollector' - | 'ImageCollector'; - -// @public -export interface OAuthDetails { - // (undocumented) - [key: string]: unknown; - // (undocumented) - code?: string; - // (undocumented) - state?: string; -} - -// @public (undocumented) -export interface ObjectOptionsCollectorWithObjectValue< - T extends ObjectValueCollectorTypes, - V = Record, - D = Record, -> { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: V; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: DeviceOptionWithDefault[]; - value?: D | null; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export interface ObjectOptionsCollectorWithStringValue< - T extends ObjectValueCollectorTypes, - V = string, -> { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: V; - type: string; - validation: ValidationRequired[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: DeviceOptionNoDefault[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type ObjectValueAutoCollector = AutoCollector< - 'ObjectValueAutoCollector', - 'ObjectValueAutoCollector', - Record ->; - -// @public (undocumented) -export type ObjectValueAutoCollectorTypes = - | 'ObjectValueAutoCollector' - | 'FidoRegistrationCollector' - | 'FidoAuthenticationCollector' - | 'MetadataCollector'; - -// @public (undocumented) -export type ObjectValueCollector = - | ObjectOptionsCollectorWithObjectValue - | ObjectOptionsCollectorWithStringValue - | ObjectValueCollectorWithObjectValue; - -// @public (undocumented) -export type ObjectValueCollectors = - | DeviceAuthenticationCollector - | DeviceRegistrationCollector - | PhoneNumberCollector - | PhoneNumberExtensionCollector - | ObjectOptionsCollectorWithObjectValue<'ObjectSelectCollector'> - | ObjectOptionsCollectorWithStringValue<'ObjectSelectCollector'>; - -// @public -export type ObjectValueCollectorTypes = - | 'DeviceAuthenticationCollector' - | 'DeviceRegistrationCollector' - | 'PhoneNumberCollector' - | 'PhoneNumberExtensionCollector' - | 'ObjectOptionsCollector' - | 'ObjectValueCollector' - | 'ObjectSelectCollector'; - -// @public (undocumented) -export interface ObjectValueCollectorWithObjectValue< - T extends ObjectValueCollectorTypes, - IV = Record, - OV = Record, -> { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: IV; - type: string; - validation: (ValidationRequired | ValidationPhoneNumber)[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value?: OV | null; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export interface OutgoingQueryParams { - // (undocumented) - [key: string]: string | string[]; -} - -// @public (undocumented) -export interface PasswordCollector { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - verify: boolean; - }; - // (undocumented) - type: 'PasswordCollector'; -} - -// @public -export type PasswordField = { - type: 'PASSWORD' | 'PASSWORD_VERIFY'; - key: string; - label: string; - required?: boolean; - verify?: boolean; - passwordPolicy?: PasswordPolicy; -}; - -// @public -export interface PasswordPolicy { - // (undocumented) - createdAt?: string; - // (undocumented) - default?: boolean; - // (undocumented) - description?: string; - // (undocumented) - excludesCommonlyUsed?: boolean; - // (undocumented) - excludesProfileData?: boolean; - // (undocumented) - history?: { - count?: number; - retentionDays?: number; - }; - // (undocumented) - id?: string; - // (undocumented) - length?: { - min?: number; - max?: number; - }; - // (undocumented) - lockout?: { - failureCount?: number; - durationSeconds?: number; - }; - // (undocumented) - maxAgeDays?: number; - // (undocumented) - maxRepeatedCharacters?: number; - // (undocumented) - minAgeDays?: number; - // (undocumented) - minCharacters?: Record; - // (undocumented) - minUniqueCharacters?: number; - // (undocumented) - name?: string; - // (undocumented) - notSimilarToCurrent?: boolean; - // (undocumented) - populationCount?: number; - // (undocumented) - updatedAt?: string; -} - -// @public (undocumented) -export type PhoneNumberCollector = ObjectValueCollectorWithObjectValue< - 'PhoneNumberCollector', - PhoneNumberInputValue, - PhoneNumberOutputValue ->; - -// @public (undocumented) -export interface PhoneNumberExtensionCollector { - // (undocumented) - category: 'ObjectValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: PhoneNumberExtensionInputValue; - type: string; - validation: (ValidationRequired | ValidationPhoneNumber)[] | null; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - extensionLabel: string; - value: PhoneNumberExtensionOutputValue; - }; - // (undocumented) - type: 'PhoneNumberExtensionCollector'; -} - -// @public (undocumented) -export type PhoneNumberExtensionField = PhoneNumberField & { - showExtension: boolean; - extensionLabel: string; -}; - -// @public (undocumented) -export interface PhoneNumberExtensionInputValue { - // (undocumented) - countryCode: string; - // (undocumented) - extension: string; - // (undocumented) - phoneNumber: string; -} - -// @public (undocumented) -export interface PhoneNumberExtensionOutputValue { - // (undocumented) - countryCode?: string; - // (undocumented) - extension?: string; - // (undocumented) - phoneNumber?: string; -} - -// @public (undocumented) -export type PhoneNumberField = { - type: 'PHONE_NUMBER'; - key: string; - label: string; - required: boolean; - defaultCountryCode: string | null; - validatePhoneNumber: boolean; -}; - -// @public (undocumented) -export interface PhoneNumberInputValue { - // (undocumented) - countryCode: string; - // (undocumented) - phoneNumber: string; -} - -// @public (undocumented) -export interface PhoneNumberOutputValue { - // (undocumented) - countryCode?: string; - // (undocumented) - phoneNumber?: string; -} - -// @public -export type Poller = () => Promise; - -// @public (undocumented) -export type PollingCollector = AutoCollector< - 'SingleValueAutoCollector', - 'PollingCollector', - string, - PollingOutputValue ->; - -// @public (undocumented) -export type PollingField = { - type: 'POLLING'; - key: string; - pollInterval: number; - pollRetries: number; - pollChallengeStatus?: boolean; - challenge?: string; -}; - -// @public (undocumented) -export interface PollingOutputValue { - // (undocumented) - challenge?: string; - // (undocumented) - pollChallengeStatus?: boolean; - // (undocumented) - pollInterval: number; - // (undocumented) - pollRetries: number; - // (undocumented) - retriesRemaining?: number; -} - -// @public (undocumented) -export type PollingStatus = PollingStatusContinue | PollingStatusChallenge; - -// @public (undocumented) -export type PollingStatusChallenge = - | PollingStatusChallengeComplete - | 'expired' - | 'timedOut' - | 'error'; - -// @public (undocumented) -export type PollingStatusChallengeComplete = - | 'approved' - | 'denied' - | 'continue' - | CustomPollingStatus; - -// @public (undocumented) -export type PollingStatusContinue = 'continue' | 'timedOut'; - -// @public (undocumented) -export type ProtectCollector = AutoCollector< - 'SingleValueAutoCollector', - 'ProtectCollector', - string, - ProtectOutputValue ->; - -// @public (undocumented) -export type ProtectField = { - type: 'PROTECT'; - key: string; - behavioralDataCollection: boolean; - universalDeviceIdentification: boolean; -}; - -// @public -export interface ProtectOutputValue { - // (undocumented) - behavioralDataCollection: boolean; - // (undocumented) - universalDeviceIdentification: boolean; -} - -// @public -export interface QrCodeCollector extends NoValueCollectorBase<'QrCodeCollector'> { - // (undocumented) - output: NoValueCollectorBase<'QrCodeCollector'>['output'] & { - src: string; - }; -} - -// @public (undocumented) -export type QrCodeField = { - type: 'QR_CODE'; - key: string; - content: string; - fallbackText?: string; -}; - -// @public -export interface ReadOnlyCollector extends NoValueCollectorBase<'ReadOnlyCollector'> { - // (undocumented) - output: NoValueCollectorBase<'ReadOnlyCollector'>['output'] & { - content: string; - title?: string; - }; -} - -// @public (undocumented) -export type ReadOnlyField = { - type: 'LABEL'; - content: string; - richContent?: RichContent; - key?: string; -}; - -// @public (undocumented) -export type ReadOnlyFields = ReadOnlyField | QrCodeField | AgreementField | ImageField; - -// @public (undocumented) -export type RedirectField = { - type: 'SOCIAL_LOGIN_BUTTON'; - key: string; - label: string; - links: Links; -}; - -// @public (undocumented) -export type RedirectFields = RedirectField; - -export { RequestMiddleware }; - -// @public -export type RichContent = { - content: string; - replacements?: Record; -}; - -// @public -export interface RichContentLink { - // (undocumented) - href: string; - // (undocumented) - key: string; - // (undocumented) - target?: '_self' | '_blank'; - // (undocumented) - type: 'link'; - // (undocumented) - value: string; -} - -// @public -export type RichContentReplacement = { - type: 'link'; - value: string; - href: string; - target?: '_self' | '_blank'; -}; - -// @public -export interface RichTextCollector extends NoValueCollectorBase<'RichTextCollector'> { - // (undocumented) - output: NoValueCollectorBase<'RichTextCollector'>['output'] & { - content: string; - richContent: CollectorRichContent; - }; -} - -// @public (undocumented) -export interface SelectorOption { - // (undocumented) - label: string; - // (undocumented) - value: string; -} - -// @public (undocumented) -export type SingleCheckboxField = { - type: 'SINGLE_CHECKBOX'; - inputType: 'BOOLEAN'; - key: string; - label: string; - required?: boolean; - errorMessage?: string; - appearance: string; - richContent?: RichContent; -}; - -// @public (undocumented) -export type SingleSelectCollector = SingleSelectCollectorWithValue<'SingleSelectCollector'>; - -// @public (undocumented) -export interface SingleSelectCollectorNoValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export interface SingleSelectCollectorWithValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: string | number | boolean; - options: SelectorOption[]; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type SingleSelectField = { - inputType: 'SINGLE_SELECT'; - key: string; - label: string; - options: { - label: string; - value: string; - }[]; - required?: boolean; - type: 'RADIO' | 'DROPDOWN'; -}; - -// @public (undocumented) -export type SingleValueAutoCollector = AutoCollector< - 'SingleValueAutoCollector', - 'SingleValueAutoCollector', - string ->; - -// @public (undocumented) -export type SingleValueAutoCollectorTypes = - | 'SingleValueAutoCollector' - | 'ProtectCollector' - | 'PollingCollector'; - -// @public -export type SingleValueCollector = - | SingleValueCollectorWithValue - | SingleValueCollectorNoValue; - -// @public (undocumented) -export interface SingleValueCollectorNoValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type SingleValueCollectors = - | PasswordCollector - | ValidatedPasswordCollector - | SingleSelectCollector - | TextCollector - | ValidatedTextCollector - | BooleanCollector - | ValidatedBooleanCollector - | SingleValueCollectorWithValue<'SingleValueCollector'>; - -// @public -export type SingleValueCollectorTypes = - | 'PasswordCollector' - | 'ValidatedPasswordCollector' - | 'BooleanCollector' - | 'ValidatedBooleanCollector' - | 'SingleValueCollector' - | 'SingleSelectCollector' - | 'SingleSelectObjectCollector' - | 'TextCollector' - | 'ValidatedTextCollector'; - -// @public (undocumented) -export interface SingleValueCollectorWithValue { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: V; - type: string; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: V; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type SingleValueFields = - | StandardField - | PasswordField - | ValidatedField - | SingleCheckboxField - | SingleSelectField - | ProtectField; - -// @public (undocumented) -export type StandardField = { - type: 'TEXT' | 'SUBMIT_BUTTON' | 'FLOW_BUTTON' | 'FLOW_LINK' | 'BUTTON'; - key: string; - label: string; - required?: boolean; -}; - -// @public (undocumented) -export interface StartNode { - // (undocumented) - cache: null; - // (undocumented) - client: { - status: 'start'; - }; - // (undocumented) - error: DaVinciError | null; - // (undocumented) - server: { - status: 'start'; - }; - // (undocumented) - status: 'start'; -} - -// @public (undocumented) -export interface StartOptions { - // (undocumented) - query: Query; -} - -// @public (undocumented) -export type SubmitCollector = ActionCollectorNoUrl<'SubmitCollector'>; - -// @public (undocumented) -export interface SuccessNode { - // (undocumented) - cache: { - key: string; - }; - // (undocumented) - client: { - authorization?: { - code?: string; - state?: string; - }; - status: 'success'; - } | null; - // (undocumented) - error: null; - // (undocumented) - httpStatus: number; - // (undocumented) - server: { - _links?: Links; - eventName?: string; - id?: string; - interactionId?: string; - interactionToken?: string; - href?: string; - session?: string; - status: 'success'; - }; - // (undocumented) - status: 'success'; -} - -// @public (undocumented) -export type TextCollector = SingleValueCollectorWithValue<'TextCollector'>; - -// @public (undocumented) -export interface ThrownQueryError { - // (undocumented) - error: FetchBaseQueryError; - // (undocumented) - isHandledError: boolean; - // (undocumented) - meta: FetchBaseQueryMeta; -} - -// @public -export type UnknownCollector = { - category: 'UnknownCollector'; - error: string | null; - type: 'UnknownCollector'; - id: string; - name: string; - output: { - key: string; - label: string; - type: string; - }; -}; - -// @public (undocumented) -export type UnknownField = Record; - -// @public -export type UpdatableCollectors = - | SingleValueCollectors - | MultiSelectCollector - | ObjectValueCollectors - | AutoCollectors; - -// @public (undocumented) -export const updateCollectorValues: ActionCreatorWithPayload< - { - id: string; - value: CollectorValueTypes; - index?: number; - }, - string ->; - -// @public -export type Updater = ( - value: CollectorValueType, - index?: number, -) => InternalErrorResponse | null; - -// @public (undocumented) -export interface ValidatedBooleanCollector extends ValidatedSingleValueCollectorWithValue< - 'ValidatedBooleanCollector', - boolean -> { - // (undocumented) - output: ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean>['output'] & { - appearance: string; - richContent?: CollectorRichContent; - }; -} - -// @public -export type ValidatedCollectors = - | ValidatedTextCollector - | ValidatedBooleanCollector - | ValidatedPasswordCollector - | ObjectValueCollectors - | MultiValueCollectors - | AutoCollectors; - -// @public (undocumented) -export type ValidatedField = { - type: 'TEXT'; - key: string; - label: string; - required: boolean; - validation: { - regex: string; - errorMessage: string; - }; -}; - -// @public (undocumented) -export interface ValidatedPasswordCollector { - // (undocumented) - category: 'SingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - value: string | number | boolean; - type: string; - validation: PasswordPolicy; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - verify: boolean; - }; - // (undocumented) - type: 'ValidatedPasswordCollector'; -} - -// @public (undocumented) -export interface ValidatedSingleValueCollectorWithValue< - T extends SingleValueCollectorTypes, - V = string, -> { - // (undocumented) - category: 'ValidatedSingleValueCollector'; - // (undocumented) - error: string | null; - // (undocumented) - id: string; - // (undocumented) - input: { - key: string; - type: string; - validation: (ValidationRequired | ValidationRegex)[]; - value: V; - }; - // (undocumented) - name: string; - // (undocumented) - output: { - key: string; - label: string; - type: string; - value: V; - }; - // (undocumented) - type: T; -} - -// @public (undocumented) -export type ValidatedTextCollector = ValidatedSingleValueCollectorWithValue<'TextCollector'>; - -// @public (undocumented) -export interface ValidationPhoneNumber { - // (undocumented) - message: string; - // (undocumented) - rule: boolean; - // (undocumented) - type: 'validatePhoneNumber'; -} - -// @public (undocumented) -export interface ValidationRegex { - // (undocumented) - message: string; - // (undocumented) - rule: string; - // (undocumented) - type: 'regex'; -} - -// @public (undocumented) -export interface ValidationRequired { - // (undocumented) - message: string; - // (undocumented) - rule: boolean; - // (undocumented) - type: 'required'; -} - -// @public -export type Validator = ( - value: CollectorValueType, -) => - | string[] - | { - error: { - message: string; - type: string; - }; - type: string; - }; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/davinci-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ActionCreatorWithPayload } from '@reduxjs/toolkit'; +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import type { DaVinciConfig } from '@forgerock/sdk-types'; +import { FetchBaseQueryError } from '@reduxjs/toolkit/query'; +import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; +import type { GenericError } from '@forgerock/sdk-types'; +import { LogLevel } from '@forgerock/sdk-logger'; +import type { MutationResultSelectorResult } from '@reduxjs/toolkit/query'; +import { QueryStatus } from '@reduxjs/toolkit/query'; +import { Reducer } from '@reduxjs/toolkit'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; +import { SerializedError } from '@reduxjs/toolkit'; +import { Unsubscribe } from '@reduxjs/toolkit'; + +// @public (undocumented) +export type ActionCollector = ActionCollectorNoUrl | ActionCollectorWithUrl; + +// @public (undocumented) +export interface ActionCollectorNoUrl { + // (undocumented) + category: 'ActionCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type ActionCollectors = ActionCollectorWithUrl<'IdpCollector'> | ActionCollectorNoUrl<'ActionCollector'> | ActionCollectorNoUrl<'FlowCollector'> | ActionCollectorNoUrl<'SubmitCollector'>; + +// @public +export type ActionCollectorTypes = 'FlowCollector' | 'SubmitCollector' | 'IdpCollector' | 'ActionCollector'; + +// @public (undocumented) +export interface ActionCollectorWithUrl { + // (undocumented) + category: 'ActionCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + url?: string | null; + }; + // (undocumented) + type: T; +} + +export { ActionTypes } + +// @public (undocumented) +export type AgreementField = { + type: 'AGREEMENT'; + key: string; + content: string; + titleEnabled: boolean; + title?: string; + agreement: { + id: string; + useDynamicAgreement: boolean; + }; + enabled: boolean; +}; + +// @public (undocumented) +export interface AssertionValue extends Omit { + // (undocumented) + rawId: string; + // (undocumented) + response: { + clientDataJSON: string; + authenticatorData: string; + signature: string; + userHandle: string | null; + }; +} + +// @public (undocumented) +export interface AttestationValue extends Omit { + // (undocumented) + rawId: string; + // (undocumented) + response: { + clientDataJSON: string; + attestationObject: string; + }; +} + +// @public (undocumented) +export interface AutoCollector> { + // (undocumented) + category: C; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: IV; + type: string; + validation?: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + type: string; + config: OV; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type AutoCollectorCategories = 'SingleValueAutoCollector' | 'ObjectValueAutoCollector'; + +// @public (undocumented) +export type AutoCollectors = ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | MetadataCollector | PollingCollector | SingleValueAutoCollector | ObjectValueAutoCollector; + +// @public (undocumented) +export type AutoCollectorTypes = SingleValueAutoCollectorTypes | ObjectValueAutoCollectorTypes; + +// @public (undocumented) +export interface BooleanCollector extends SingleValueCollectorWithValue<'BooleanCollector', boolean> { + // (undocumented) + output: SingleValueCollectorWithValue<'BooleanCollector', boolean>['output'] & { + appearance: string; + richContent?: CollectorRichContent; + }; +} + +// @public (undocumented) +export type CollectorCategory = Collectors['category']; + +// @public (undocumented) +export interface CollectorErrors { + // (undocumented) + code: string; + // (undocumented) + message: string; + // (undocumented) + target: string; +} + +// @public +export interface CollectorRichContent { + // (undocumented) + content: string; + // (undocumented) + replacements: RichContentLink[]; +} + +// @public (undocumented) +export type Collectors = FlowCollector | MetadataCollector | PasswordCollector | ValidatedPasswordCollector | TextCollector | BooleanCollector | ValidatedBooleanCollector | SingleSelectCollector | IdpCollector | SubmitCollector | ActionCollector<'ActionCollector'> | SingleValueCollector<'SingleValueCollector'> | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ReadOnlyCollector | RichTextCollector | ValidatedTextCollector | ProtectCollector | PollingCollector | FidoRegistrationCollector | FidoAuthenticationCollector | QrCodeCollector | ImageCollector | UnknownCollector; + +// @public +export type CollectorValueType = T extends { + type: 'PasswordCollector'; +} | { + type: 'ValidatedPasswordCollector'; +} | { + type: 'SingleSelectCollector'; +} | { + type: 'DeviceRegistrationCollector'; +} | { + type: 'DeviceAuthenticationCollector'; +} | { + type: 'ProtectCollector'; +} | { + type: 'PollingCollector'; +} ? string : T extends { + type: 'TextCollector'; + category: 'SingleValueCollector'; +} ? string : T extends { + type: 'TextCollector'; + category: 'ValidatedSingleValueCollector'; +} ? string : T extends { + type: 'BooleanCollector'; +} | { + type: 'ValidatedBooleanCollector'; +} ? boolean : T extends { + type: 'MultiSelectCollector'; +} ? string | string[] : T extends { + type: 'PhoneNumberCollector'; +} ? PhoneNumberInputValue : T extends { + type: 'PhoneNumberExtensionCollector'; +} ? PhoneNumberExtensionInputValue : T extends { + type: 'FidoRegistrationCollector'; +} ? FidoRegistrationInputValue | GenericError : T extends { + type: 'FidoAuthenticationCollector'; +} ? FidoAuthenticationInputValue | GenericError : T extends { + type: 'MetadataCollector'; +} ? Record | MetadataError : T extends { + category: 'SingleValueCollector'; +} ? string : T extends { + category: 'ValidatedSingleValueCollector'; +} ? string : T extends { + category: 'SingleValueAutoCollector'; +} ? string : T extends { + category: 'MultiValueCollector'; +} ? string | string[] : T extends { + category: 'ActionCollector'; +} ? never : T extends { + category: 'NoValueCollector'; +} ? never : CollectorValueTypes; + +// @public +export type CollectorValueTypes = string | string[] | boolean | PhoneNumberInputValue | PhoneNumberExtensionInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue | MetadataError | GenericError; + +// @public (undocumented) +export type ComplexValueFields = DeviceAuthenticationField | DeviceRegistrationField | PhoneNumberField | PhoneNumberExtensionField | FidoRegistrationField | FidoAuthenticationField | PollingField | MetadataField; + +// @public (undocumented) +export interface ContinueNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: 'continue'; + }; + // (undocumented) + error: null; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + eventName?: string; + status: 'continue'; + }; + // (undocumented) + status: 'continue'; +} + +export { CustomLogger } + +// @public +export type CustomPollingStatus = string & {}; + +// @public +export function davinci(input: { + config: DaVinciConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; + store?: unknown; +}): Promise<{ + error: string; + type: "argument_error"; +} | { + store: SdkStore; + subscribe: (listener: () => void) => Unsubscribe; + externalIdp: () => (() => Promise); + flow: (action: DaVinciAction) => InitFlow; + next: (args?: DaVinciRequest) => Promise; + resume: (input: { + continueToken: string; + }) => Promise; + start: (options?: StartOptions | undefined) => Promise; + update: (collector: T) => Updater; + validate: (collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors | AutoCollectors) => Validator; + pollStatus: (collector: PollingCollector) => Poller; + getClient: () => { + status: "start"; + } | { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: "error"; + } | { + status: "failure"; + } | { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: "continue"; + } | { + authorization?: { + code?: string; + state?: string; + }; + status: "success"; + } | null; + getCollectors: () => Collectors[]; + getError: () => DaVinciError | null; + getErrorCollectors: () => CollectorErrors[]; + getNode: () => StartNode | ErrorNode | FailureNode | ContinueNode | SuccessNode; + getServer: () => { + status: "start"; + } | { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: "error"; + } | { + _links?: Links; + eventName?: string; + href?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: "failure"; + } | { + _links?: Links; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + eventName?: string; + status: "continue"; + } | { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + session?: string; + status: "success"; + } | null; + cache: { + getLatestResponse: () => ({ + status: QueryStatus.fulfilled; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "data" | "fulfilledTimeStamp"> & Required> & { + error: undefined; + } & { + status: QueryStatus.fulfilled; + isUninitialized: false; + isLoading: false; + isSuccess: true; + isError: false; + }) | ({ + status: QueryStatus.rejected; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "error"> & Required> & { + status: QueryStatus.rejected; + isUninitialized: false; + isLoading: false; + isSuccess: false; + isError: true; + }) | { + error: { + message: string; + type: string; + }; + }; + getResponseWithId: (requestId: string) => ({ + status: QueryStatus.fulfilled; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "data" | "fulfilledTimeStamp"> & Required> & { + error: undefined; + } & { + status: QueryStatus.fulfilled; + isUninitialized: false; + isLoading: false; + isSuccess: true; + isError: false; + }) | ({ + status: QueryStatus.rejected; + } & Omit<{ + requestId: string; + data?: unknown; + error?: FetchBaseQueryError | SerializedError | undefined; + endpointName: string; + startedTimeStamp: number; + fulfilledTimeStamp?: number; + }, "error"> & Required> & { + status: QueryStatus.rejected; + isUninitialized: false; + isLoading: false; + isSuccess: false; + isError: true; + }) | { + error: { + message: string; + type: string; + }; + }; + }; +}>; + +// @public +export interface DaVinciAction { + // (undocumented) + action: string; +} + +// @public +export interface DaVinciBaseResponse { + // (undocumented) + capabilityName?: string; + // (undocumented) + companyId?: string; + // (undocumented) + connectionId?: string; + // (undocumented) + connectorId?: string; + // (undocumented) + id?: string; + // (undocumented) + interactionId?: string; + // (undocumented) + interactionToken?: string; + // (undocumented) + isResponseCompatibleWithMobileAndWebSdks?: boolean; + // (undocumented) + status?: string; +} + +// @public +export type DaVinciCacheEntry = { + data?: DaVinciBaseResponse; + error?: { + data: DaVinciBaseResponse; + status: number; + }; +} & { + data?: any; + error?: any; +} & MutationResultSelectorResult; + +// @public (undocumented) +export type DavinciClient = Awaited>; + +export { DaVinciConfig } + +// @public (undocumented) +export interface DaVinciError extends Omit { + // (undocumented) + collectors?: CollectorErrors[]; + // (undocumented) + internalHttpStatus?: number; + // (undocumented) + message: string; + // (undocumented) + status: 'error' | 'failure' | 'unknown'; +} + +// @public (undocumented) +export interface DaVinciErrorCacheEntry { + // (undocumented) + endpointName: 'next' | 'flow' | 'start'; + // (undocumented) + error: { + data: T; + }; + // (undocumented) + fulfilledTimeStamp: number; + // (undocumented) + isError: boolean; + // (undocumented) + isLoading: boolean; + // (undocumented) + isSuccess: boolean; + // (undocumented) + isUninitialized: boolean; + // (undocumented) + requestId: string; + // (undocumented) + startedTimeStamp: number; + // (undocumented) + status: 'fulfilled' | 'pending' | 'rejected'; +} + +// @public (undocumented) +export interface DavinciErrorResponse extends DaVinciBaseResponse { + // (undocumented) + cause?: string | null; + // (undocumented) + code: string | number; + // (undocumented) + details?: ErrorDetail[]; + // (undocumented) + doNotSendToOE?: boolean; + // (undocumented) + error?: { + code?: string; + message?: string; + }; + // (undocumented) + errorCategory?: string; + // (undocumented) + errorMessage?: string; + // (undocumented) + expected?: boolean; + // (undocumented) + httpResponseCode: number; + // (undocumented) + isErrorCustomized?: boolean; + // (undocumented) + message: string; + // (undocumented) + metricAttributes?: { + [key: string]: unknown; + }; +} + +// @public (undocumented) +export interface DaVinciFailureResponse extends DaVinciBaseResponse { + // (undocumented) + error?: { + code?: string; + message?: string; + [key: string]: unknown; + }; +} + +// @public (undocumented) +export type DaVinciField = ComplexValueFields | MultiValueFields | ReadOnlyFields | RedirectFields | SingleValueFields; + +// @public +export interface DaVinciNextResponse extends DaVinciBaseResponse { + // (undocumented) + eventName?: string; + // (undocumented) + form?: { + name?: string; + description?: string; + components?: { + fields?: DaVinciField[]; + }; + }; + // (undocumented) + formData?: { + value?: { + [key: string]: string; + }; + }; + // (undocumented) + _links?: Links; +} + +// @public +export interface DaVinciPollResponse extends DaVinciBaseResponse { + // (undocumented) + eventName?: string; + // (undocumented) + _links?: Links; + // (undocumented) + success?: boolean; +} + +// @public (undocumented) +export interface DaVinciRequest { + // (undocumented) + eventName: string; + // (undocumented) + id: string; + // (undocumented) + interactionId: string; + // (undocumented) + parameters: { + eventType: 'submit' | 'action' | 'polling'; + data: { + actionKey: string; + formData?: Record; + }; + }; +} + +// @public +export type DaVinciRequestValueTypes = string | number | boolean | (string | number | boolean)[] | DeviceValue | PhoneNumberInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue | MetadataError | GenericError; + +// @public (undocumented) +export interface DaVinciSuccessResponse extends DaVinciBaseResponse { + // (undocumented) + authorizeResponse?: OAuthDetails; + // (undocumented) + environment: { + id: string; + [key: string]: unknown; + }; + // (undocumented) + _links?: Links; + // (undocumented) + resetCookie?: boolean; + // (undocumented) + session?: { + id?: string; + [key: string]: unknown; + }; + // (undocumented) + sessionToken?: string; + // (undocumented) + sessionTokenMaxAge?: number; + // (undocumented) + status: string; + // (undocumented) + subFlowSettings?: { + cssLinks?: unknown[]; + cssUrl?: unknown; + jsLinks?: unknown[]; + loadingScreenSettings?: unknown; + reactSkUrl?: unknown; + }; + // (undocumented) + success: true; +} + +// @public (undocumented) +export type DeviceAuthenticationCollector = ObjectOptionsCollectorWithObjectValue<'DeviceAuthenticationCollector', DeviceValue>; + +// @public (undocumented) +export type DeviceAuthenticationField = { + type: 'DEVICE_AUTHENTICATION'; + key: string; + label: string; + options: { + type: string; + iconSrc: string; + title: string; + id: string; + default: boolean; + description: string; + }[]; + required: boolean; +}; + +// @public (undocumented) +export interface DeviceOptionNoDefault { + // (undocumented) + content: string; + // (undocumented) + key: string; + // (undocumented) + label: string; + // (undocumented) + type: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export interface DeviceOptionWithDefault { + // (undocumented) + content: string; + // (undocumented) + default: boolean; + // (undocumented) + key: string; + // (undocumented) + label: string; + // (undocumented) + type: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export type DeviceRegistrationCollector = ObjectOptionsCollectorWithStringValue<'DeviceRegistrationCollector', string>; + +// @public (undocumented) +export type DeviceRegistrationField = { + type: 'DEVICE_REGISTRATION'; + key: string; + label: string; + options: { + type: string; + iconSrc: string; + title: string; + description: string; + }[]; + required: boolean; +}; + +// @public (undocumented) +export interface DeviceValue { + // (undocumented) + id: string; + // (undocumented) + type: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export interface ErrorDetail { + // (undocumented) + message?: string; + // (undocumented) + rawResponse?: { + _embedded?: { + users?: Array; + }; + code?: string; + count?: number; + details?: NestedErrorDetails[]; + id?: string; + message?: string; + size?: number; + userFilter?: string; + [key: string]: unknown; + }; + // (undocumented) + statusCode?: number; +} + +// @public (undocumented) +export interface ErrorNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + action: string; + collectors: Collectors[]; + description?: string; + name?: string; + status: 'error'; + }; + // (undocumented) + error: DaVinciError; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: 'error'; + } | null; + // (undocumented) + status: 'error'; +} + +// @public (undocumented) +export interface FailureNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + status: 'failure'; + }; + // (undocumented) + error: DaVinciError; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + eventName?: string; + href?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + status: 'failure'; + } | null; + // (undocumented) + status: 'failure'; +} + +// @public (undocumented) +export type FidoAuthenticationCollector = AutoCollector<'ObjectValueAutoCollector', 'FidoAuthenticationCollector', FidoAuthenticationInputValue | GenericError, FidoAuthenticationOutputValue>; + +// @public (undocumented) +export type FidoAuthenticationField = { + type: 'FIDO2'; + key: string; + label: string; + publicKeyCredentialRequestOptions: FidoAuthenticationOptions; + action: 'AUTHENTICATE'; + trigger: string; + required: boolean; +}; + +// @public (undocumented) +export interface FidoAuthenticationInputValue { + // (undocumented) + assertionValue?: AssertionValue; +} + +// @public (undocumented) +export interface FidoAuthenticationOptions extends Omit { + // (undocumented) + allowCredentials?: { + id: number[]; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; + }[]; + // (undocumented) + challenge: number[]; +} + +// @public (undocumented) +export interface FidoAuthenticationOutputValue { + // (undocumented) + action: 'AUTHENTICATE'; + // (undocumented) + publicKeyCredentialRequestOptions: FidoAuthenticationOptions; + // (undocumented) + trigger: string; +} + +// @public (undocumented) +export interface FidoClient { + authenticate: (options: FidoAuthenticationOptions) => Promise; + register: (options: FidoRegistrationOptions) => Promise; +} + +// @public +export type FidoErrorCode = 'NotAllowedError' | 'AbortError' | 'InvalidStateError' | 'NotSupportedError' | 'SecurityError' | 'TimeoutError' | 'UnknownError'; + +// @public (undocumented) +export type FidoRegistrationCollector = AutoCollector<'ObjectValueAutoCollector', 'FidoRegistrationCollector', FidoRegistrationInputValue | GenericError, FidoRegistrationOutputValue>; + +// @public (undocumented) +export type FidoRegistrationField = { + type: 'FIDO2'; + key: string; + label: string; + publicKeyCredentialCreationOptions: FidoRegistrationOptions; + action: 'REGISTER'; + trigger: string; + required: boolean; +}; + +// @public (undocumented) +export interface FidoRegistrationInputValue { + // (undocumented) + attestationValue?: AttestationValue; +} + +// @public (undocumented) +export interface FidoRegistrationOptions extends Omit { + // (undocumented) + challenge: number[]; + // (undocumented) + excludeCredentials?: { + id: number[]; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; + }[]; + // (undocumented) + pubKeyCredParams: { + alg: string | number; + type: PublicKeyCredentialType; + }[]; + // (undocumented) + user: { + id: number[]; + name: string; + displayName: string; + }; +} + +// @public (undocumented) +export interface FidoRegistrationOutputValue { + // (undocumented) + action: 'REGISTER'; + // (undocumented) + publicKeyCredentialCreationOptions: FidoRegistrationOptions; + // (undocumented) + trigger: string; +} + +// @public (undocumented) +export type FlowCollector = ActionCollectorNoUrl<'FlowCollector'>; + +// @public (undocumented) +export type FlowNode = ContinueNode | ErrorNode | StartNode | SuccessNode | FailureNode; + +// @public +export type GetClient = StartNode['client'] | ContinueNode['client'] | ErrorNode['client'] | SuccessNode['client'] | FailureNode['client']; + +// @public (undocumented) +export type IdpCollector = ActionCollectorWithUrl<'IdpCollector'>; + +// @public +export interface ImageCollector extends NoValueCollectorBase<'ImageCollector'> { + // (undocumented) + output: NoValueCollectorBase<'ImageCollector'>['output'] & { + src: string; + alt: string; + href?: string; + }; +} + +// @public (undocumented) +export type ImageField = { + type: 'IMAGE'; + key: string; + description: string; + imageUrl: string; + hyperlinkUrl?: string; +}; + +// @public (undocumented) +export type InferActionCollectorType = T extends 'IdpCollector' ? IdpCollector : T extends 'SubmitCollector' ? SubmitCollector : T extends 'FlowCollector' ? FlowCollector : ActionCollectorWithUrl<'ActionCollector'> | ActionCollectorNoUrl<'ActionCollector'>; + +// @public +export type InferAutoCollectorType = T extends 'ProtectCollector' ? ProtectCollector : T extends 'PollingCollector' ? PollingCollector : T extends 'FidoRegistrationCollector' ? FidoRegistrationCollector : T extends 'FidoAuthenticationCollector' ? FidoAuthenticationCollector : T extends 'MetadataCollector' ? MetadataCollector : T extends 'ObjectValueAutoCollector' ? ObjectValueAutoCollector : SingleValueAutoCollector; + +// @public +export type InferMultiValueCollectorType = T extends 'MultiSelectCollector' ? MultiValueCollectorWithValue<'MultiSelectCollector'> : MultiValueCollectorWithValue<'MultiValueCollector'> | MultiValueCollectorNoValue<'MultiValueCollector'>; + +// @public +export type InferNoValueCollectorType = T extends 'ReadOnlyCollector' ? ReadOnlyCollector : T extends 'RichTextCollector' ? RichTextCollector : T extends 'QrCodeCollector' ? QrCodeCollector : T extends 'ImageCollector' ? ImageCollector : NoValueCollectorBase<'NoValueCollector'>; + +// @public +export type InferSingleValueCollectorType = T extends 'TextCollector' ? TextCollector : T extends 'SingleSelectCollector' ? SingleSelectCollector : T extends 'ValidatedTextCollector' ? ValidatedTextCollector : T extends 'PasswordCollector' ? PasswordCollector : T extends 'ValidatedPasswordCollector' ? ValidatedPasswordCollector : T extends 'BooleanCollector' ? BooleanCollector : T extends 'ValidatedBooleanCollector' ? ValidatedBooleanCollector : SingleValueCollectorWithValue<'SingleValueCollector'> | SingleValueCollectorNoValue<'SingleValueCollector'>; + +// @public (undocumented) +export type InferValueObjectCollectorType = T extends 'DeviceAuthenticationCollector' ? DeviceAuthenticationCollector : T extends 'DeviceRegistrationCollector' ? DeviceRegistrationCollector : T extends 'PhoneNumberCollector' ? PhoneNumberCollector : T extends 'PhoneNumberExtensionCollector' ? PhoneNumberExtensionCollector : ObjectOptionsCollectorWithObjectValue<'ObjectValueCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectValueCollector'>; + +// @public (undocumented) +export type InitFlow = () => Promise; + +// @public (undocumented) +export interface InternalErrorResponse { + // (undocumented) + error: Omit & { + message: string; + }; + // (undocumented) + type: 'internal_error'; +} + +// @public (undocumented) +export interface Links { + // (undocumented) + [key: string]: { + href?: string; + }; +} + +export { LogLevel } + +// @public (undocumented) +export type MetadataCollector = AutoCollector<'ObjectValueAutoCollector', 'MetadataCollector', MetadataCollectorInputValue, Record>; + +// @public (undocumented) +export type MetadataCollectorInputValue = Record | MetadataError; + +// @public +export interface MetadataError { + // (undocumented) + code: string; + // (undocumented) + message: string; +} + +// @public (undocumented) +export type MetadataField = { + type: 'METADATA'; + key: string; + payload: Record; +}; + +// @public (undocumented) +export type MultiSelectCollector = MultiValueCollectorWithValue<'MultiSelectCollector'>; + +// @public (undocumented) +export type MultiSelectField = { + inputType: 'MULTI_SELECT'; + key: string; + label: string; + options: { + label: string; + value: string; + }[]; + required?: boolean; + type: 'CHECKBOX' | 'COMBOBOX'; +}; + +// @public (undocumented) +export type MultiValueCollector = MultiValueCollectorWithValue | MultiValueCollectorNoValue; + +// @public (undocumented) +export interface MultiValueCollectorNoValue { + // (undocumented) + category: 'MultiValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string[]; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type MultiValueCollectors = MultiValueCollectorWithValue<'MultiValueCollector'> | MultiValueCollectorWithValue<'MultiSelectCollector'>; + +// @public +export type MultiValueCollectorTypes = 'MultiSelectCollector' | 'MultiValueCollector'; + +// @public (undocumented) +export interface MultiValueCollectorWithValue { + // (undocumented) + category: 'MultiValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string[]; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: string[]; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type MultiValueFields = MultiSelectField; + +// @public +export interface NestedErrorDetails { + // (undocumented) + code?: string; + // (undocumented) + innerError?: { + history?: string; + unsatisfiedRequirements?: string[]; + failuresRemaining?: number; + }; + // (undocumented) + message?: string; + // (undocumented) + target?: string; +} + +// @public +export const nextCollectorValues: ActionCreatorWithPayload< { +fields: DaVinciField[]; +formData: { +value: Record; +}; +}, string>; + +// @public +export const nodeCollectorReducer: Reducer & { + getInitialState: () => Collectors[]; +}; + +// @public (undocumented) +export type NodeStates = StartNode | ContinueNode | ErrorNode | SuccessNode | FailureNode; + +// @public (undocumented) +export type NoValueCollector = InferNoValueCollectorType; + +// @public (undocumented) +export interface NoValueCollectorBase { + // (undocumented) + category: 'NoValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type NoValueCollectors = NoValueCollectorBase<'NoValueCollector'> | ReadOnlyCollector | RichTextCollector | QrCodeCollector | ImageCollector; + +// @public +export type NoValueCollectorTypes = 'ReadOnlyCollector' | 'RichTextCollector' | 'NoValueCollector' | 'QrCodeCollector' | 'ImageCollector'; + +// @public +export interface OAuthDetails { + // (undocumented) + [key: string]: unknown; + // (undocumented) + code?: string; + // (undocumented) + state?: string; +} + +// @public (undocumented) +export interface ObjectOptionsCollectorWithObjectValue, D = Record> { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: V; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: DeviceOptionWithDefault[]; + value?: D | null; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export interface ObjectOptionsCollectorWithStringValue { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: V; + type: string; + validation: ValidationRequired[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: DeviceOptionNoDefault[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type ObjectValueAutoCollector = AutoCollector<'ObjectValueAutoCollector', 'ObjectValueAutoCollector', Record>; + +// @public (undocumented) +export type ObjectValueAutoCollectorTypes = 'ObjectValueAutoCollector' | 'FidoRegistrationCollector' | 'FidoAuthenticationCollector' | 'MetadataCollector'; + +// @public (undocumented) +export type ObjectValueCollector = ObjectOptionsCollectorWithObjectValue | ObjectOptionsCollectorWithStringValue | ObjectValueCollectorWithObjectValue; + +// @public (undocumented) +export type ObjectValueCollectors = DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ObjectOptionsCollectorWithObjectValue<'ObjectSelectCollector'> | ObjectOptionsCollectorWithStringValue<'ObjectSelectCollector'>; + +// @public +export type ObjectValueCollectorTypes = 'DeviceAuthenticationCollector' | 'DeviceRegistrationCollector' | 'PhoneNumberCollector' | 'PhoneNumberExtensionCollector' | 'ObjectOptionsCollector' | 'ObjectValueCollector' | 'ObjectSelectCollector'; + +// @public (undocumented) +export interface ObjectValueCollectorWithObjectValue, OV = Record> { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: IV; + type: string; + validation: (ValidationRequired | ValidationPhoneNumber)[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value?: OV | null; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export interface OutgoingQueryParams { + // (undocumented) + [key: string]: string | string[]; +} + +// @public (undocumented) +export interface PasswordCollector { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + verify: boolean; + }; + // (undocumented) + type: 'PasswordCollector'; +} + +// @public +export type PasswordField = { + type: 'PASSWORD' | 'PASSWORD_VERIFY'; + key: string; + label: string; + required?: boolean; + verify?: boolean; + passwordPolicy?: PasswordPolicy; +}; + +// @public +export interface PasswordPolicy { + // (undocumented) + createdAt?: string; + // (undocumented) + default?: boolean; + // (undocumented) + description?: string; + // (undocumented) + excludesCommonlyUsed?: boolean; + // (undocumented) + excludesProfileData?: boolean; + // (undocumented) + history?: { + count?: number; + retentionDays?: number; + }; + // (undocumented) + id?: string; + // (undocumented) + length?: { + min?: number; + max?: number; + }; + // (undocumented) + lockout?: { + failureCount?: number; + durationSeconds?: number; + }; + // (undocumented) + maxAgeDays?: number; + // (undocumented) + maxRepeatedCharacters?: number; + // (undocumented) + minAgeDays?: number; + // (undocumented) + minCharacters?: Record; + // (undocumented) + minUniqueCharacters?: number; + // (undocumented) + name?: string; + // (undocumented) + notSimilarToCurrent?: boolean; + // (undocumented) + populationCount?: number; + // (undocumented) + updatedAt?: string; +} + +// @public (undocumented) +export type PhoneNumberCollector = ObjectValueCollectorWithObjectValue<'PhoneNumberCollector', PhoneNumberInputValue, PhoneNumberOutputValue>; + +// @public (undocumented) +export interface PhoneNumberExtensionCollector { + // (undocumented) + category: 'ObjectValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: PhoneNumberExtensionInputValue; + type: string; + validation: (ValidationRequired | ValidationPhoneNumber)[] | null; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + extensionLabel: string; + value: PhoneNumberExtensionOutputValue; + }; + // (undocumented) + type: 'PhoneNumberExtensionCollector'; +} + +// @public (undocumented) +export type PhoneNumberExtensionField = PhoneNumberField & { + showExtension: boolean; + extensionLabel: string; +}; + +// @public (undocumented) +export interface PhoneNumberExtensionInputValue { + // (undocumented) + countryCode: string; + // (undocumented) + extension: string; + // (undocumented) + phoneNumber: string; +} + +// @public (undocumented) +export interface PhoneNumberExtensionOutputValue { + // (undocumented) + countryCode?: string; + // (undocumented) + extension?: string; + // (undocumented) + phoneNumber?: string; +} + +// @public (undocumented) +export type PhoneNumberField = { + type: 'PHONE_NUMBER'; + key: string; + label: string; + required: boolean; + defaultCountryCode: string | null; + validatePhoneNumber: boolean; +}; + +// @public (undocumented) +export interface PhoneNumberInputValue { + // (undocumented) + countryCode: string; + // (undocumented) + phoneNumber: string; +} + +// @public (undocumented) +export interface PhoneNumberOutputValue { + // (undocumented) + countryCode?: string; + // (undocumented) + phoneNumber?: string; +} + +// @public +export type Poller = () => Promise; + +// @public (undocumented) +export type PollingCollector = AutoCollector<'SingleValueAutoCollector', 'PollingCollector', string, PollingOutputValue>; + +// @public (undocumented) +export type PollingField = { + type: 'POLLING'; + key: string; + pollInterval: number; + pollRetries: number; + pollChallengeStatus?: boolean; + challenge?: string; +}; + +// @public (undocumented) +export interface PollingOutputValue { + // (undocumented) + challenge?: string; + // (undocumented) + pollChallengeStatus?: boolean; + // (undocumented) + pollInterval: number; + // (undocumented) + pollRetries: number; + // (undocumented) + retriesRemaining?: number; +} + +// @public (undocumented) +export type PollingStatus = PollingStatusContinue | PollingStatusChallenge; + +// @public (undocumented) +export type PollingStatusChallenge = PollingStatusChallengeComplete | 'expired' | 'timedOut' | 'error'; + +// @public (undocumented) +export type PollingStatusChallengeComplete = 'approved' | 'denied' | 'continue' | CustomPollingStatus; + +// @public (undocumented) +export type PollingStatusContinue = 'continue' | 'timedOut'; + +// @public (undocumented) +export type ProtectCollector = AutoCollector<'SingleValueAutoCollector', 'ProtectCollector', string, ProtectOutputValue>; + +// @public (undocumented) +export type ProtectField = { + type: 'PROTECT'; + key: string; + behavioralDataCollection: boolean; + universalDeviceIdentification: boolean; +}; + +// @public +export interface ProtectOutputValue { + // (undocumented) + behavioralDataCollection: boolean; + // (undocumented) + universalDeviceIdentification: boolean; +} + +// @public +export interface QrCodeCollector extends NoValueCollectorBase<'QrCodeCollector'> { + // (undocumented) + output: NoValueCollectorBase<'QrCodeCollector'>['output'] & { + src: string; + }; +} + +// @public (undocumented) +export type QrCodeField = { + type: 'QR_CODE'; + key: string; + content: string; + fallbackText?: string; +}; + +// @public +export interface ReadOnlyCollector extends NoValueCollectorBase<'ReadOnlyCollector'> { + // (undocumented) + output: NoValueCollectorBase<'ReadOnlyCollector'>['output'] & { + content: string; + title?: string; + }; +} + +// @public (undocumented) +export type ReadOnlyField = { + type: 'LABEL'; + content: string; + richContent?: RichContent; + key?: string; +}; + +// @public (undocumented) +export type ReadOnlyFields = ReadOnlyField | QrCodeField | AgreementField | ImageField; + +// @public (undocumented) +export type RedirectField = { + type: 'SOCIAL_LOGIN_BUTTON'; + key: string; + label: string; + links: Links; +}; + +// @public (undocumented) +export type RedirectFields = RedirectField; + +export { RequestMiddleware } + +// @public +export type RichContent = { + content: string; + replacements?: Record; +}; + +// @public +export interface RichContentLink { + // (undocumented) + href: string; + // (undocumented) + key: string; + // (undocumented) + target?: '_self' | '_blank'; + // (undocumented) + type: 'link'; + // (undocumented) + value: string; +} + +// @public +export type RichContentReplacement = { + type: 'link'; + value: string; + href: string; + target?: '_self' | '_blank'; +}; + +// @public +export interface RichTextCollector extends NoValueCollectorBase<'RichTextCollector'> { + // (undocumented) + output: NoValueCollectorBase<'RichTextCollector'>['output'] & { + content: string; + richContent: CollectorRichContent; + }; +} + +// @public (undocumented) +export interface SelectorOption { + // (undocumented) + label: string; + // (undocumented) + value: string; +} + +// @public (undocumented) +export type SingleCheckboxField = { + type: 'SINGLE_CHECKBOX'; + inputType: 'BOOLEAN'; + key: string; + label: string; + required?: boolean; + errorMessage?: string; + appearance: string; + richContent?: RichContent; +}; + +// @public (undocumented) +export type SingleSelectCollector = SingleSelectCollectorWithValue<'SingleSelectCollector'>; + +// @public (undocumented) +export interface SingleSelectCollectorNoValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export interface SingleSelectCollectorWithValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: string | number | boolean; + options: SelectorOption[]; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type SingleSelectField = { + inputType: 'SINGLE_SELECT'; + key: string; + label: string; + options: { + label: string; + value: string; + }[]; + required?: boolean; + type: 'RADIO' | 'DROPDOWN'; +}; + +// @public (undocumented) +export type SingleValueAutoCollector = AutoCollector<'SingleValueAutoCollector', 'SingleValueAutoCollector', string>; + +// @public (undocumented) +export type SingleValueAutoCollectorTypes = 'SingleValueAutoCollector' | 'ProtectCollector' | 'PollingCollector'; + +// @public +export type SingleValueCollector = SingleValueCollectorWithValue | SingleValueCollectorNoValue; + +// @public (undocumented) +export interface SingleValueCollectorNoValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type SingleValueCollectors = PasswordCollector | ValidatedPasswordCollector | SingleSelectCollector | TextCollector | ValidatedTextCollector | BooleanCollector | ValidatedBooleanCollector | SingleValueCollectorWithValue<'SingleValueCollector'>; + +// @public +export type SingleValueCollectorTypes = 'PasswordCollector' | 'ValidatedPasswordCollector' | 'BooleanCollector' | 'ValidatedBooleanCollector' | 'SingleValueCollector' | 'SingleSelectCollector' | 'SingleSelectObjectCollector' | 'TextCollector' | 'ValidatedTextCollector'; + +// @public (undocumented) +export interface SingleValueCollectorWithValue { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: V; + type: string; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: V; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type SingleValueFields = StandardField | PasswordField | ValidatedField | SingleCheckboxField | SingleSelectField | ProtectField; + +// @public (undocumented) +export type StandardField = { + type: 'TEXT' | 'SUBMIT_BUTTON' | 'FLOW_BUTTON' | 'FLOW_LINK' | 'BUTTON'; + key: string; + label: string; + required?: boolean; +}; + +// @public (undocumented) +export interface StartNode { + // (undocumented) + cache: null; + // (undocumented) + client: { + status: 'start'; + }; + // (undocumented) + error: DaVinciError | null; + // (undocumented) + server: { + status: 'start'; + }; + // (undocumented) + status: 'start'; +} + +// @public (undocumented) +export interface StartOptions { + // (undocumented) + query: Query; +} + +// @public (undocumented) +export type SubmitCollector = ActionCollectorNoUrl<'SubmitCollector'>; + +// @public (undocumented) +export interface SuccessNode { + // (undocumented) + cache: { + key: string; + }; + // (undocumented) + client: { + authorization?: { + code?: string; + state?: string; + }; + status: 'success'; + } | null; + // (undocumented) + error: null; + // (undocumented) + httpStatus: number; + // (undocumented) + server: { + _links?: Links; + eventName?: string; + id?: string; + interactionId?: string; + interactionToken?: string; + href?: string; + session?: string; + status: 'success'; + }; + // (undocumented) + status: 'success'; +} + +// @public (undocumented) +export type TextCollector = SingleValueCollectorWithValue<'TextCollector'>; + +// @public (undocumented) +export interface ThrownQueryError { + // (undocumented) + error: FetchBaseQueryError; + // (undocumented) + isHandledError: boolean; + // (undocumented) + meta: FetchBaseQueryMeta; +} + +// @public +export type UnknownCollector = { + category: 'UnknownCollector'; + error: string | null; + type: 'UnknownCollector'; + id: string; + name: string; + output: { + key: string; + label: string; + type: string; + }; +}; + +// @public (undocumented) +export type UnknownField = Record; + +// @public +export type UpdatableCollectors = SingleValueCollectors | MultiSelectCollector | ObjectValueCollectors | AutoCollectors; + +// @public (undocumented) +export const updateCollectorValues: ActionCreatorWithPayload< { +id: string; +value: CollectorValueTypes; +index?: number; +}, string>; + +// @public +export type Updater = (value: CollectorValueType, index?: number) => InternalErrorResponse | null; + +// @public (undocumented) +export interface ValidatedBooleanCollector extends ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean> { + // (undocumented) + output: ValidatedSingleValueCollectorWithValue<'ValidatedBooleanCollector', boolean>['output'] & { + appearance: string; + richContent?: CollectorRichContent; + }; +} + +// @public +export type ValidatedCollectors = ValidatedTextCollector | ValidatedBooleanCollector | ValidatedPasswordCollector | ObjectValueCollectors | MultiValueCollectors | AutoCollectors; + +// @public (undocumented) +export type ValidatedField = { + type: 'TEXT'; + key: string; + label: string; + required: boolean; + validation: { + regex: string; + errorMessage: string; + }; +}; + +// @public (undocumented) +export interface ValidatedPasswordCollector { + // (undocumented) + category: 'SingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + value: string | number | boolean; + type: string; + validation: PasswordPolicy; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + verify: boolean; + }; + // (undocumented) + type: 'ValidatedPasswordCollector'; +} + +// @public (undocumented) +export interface ValidatedSingleValueCollectorWithValue { + // (undocumented) + category: 'ValidatedSingleValueCollector'; + // (undocumented) + error: string | null; + // (undocumented) + id: string; + // (undocumented) + input: { + key: string; + type: string; + validation: (ValidationRequired | ValidationRegex)[]; + value: V; + }; + // (undocumented) + name: string; + // (undocumented) + output: { + key: string; + label: string; + type: string; + value: V; + }; + // (undocumented) + type: T; +} + +// @public (undocumented) +export type ValidatedTextCollector = ValidatedSingleValueCollectorWithValue<'TextCollector'>; + +// @public (undocumented) +export interface ValidationPhoneNumber { + // (undocumented) + message: string; + // (undocumented) + rule: boolean; + // (undocumented) + type: 'validatePhoneNumber'; +} + +// @public (undocumented) +export interface ValidationRegex { + // (undocumented) + message: string; + // (undocumented) + rule: string; + // (undocumented) + type: 'regex'; +} + +// @public (undocumented) +export interface ValidationRequired { + // (undocumented) + message: string; + // (undocumented) + rule: boolean; + // (undocumented) + type: 'required'; +} + +// @public +export type Validator = (value: CollectorValueType) => string[] | { + error: { + message: string; + type: string; + }; + type: string; +}; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/davinci-client/package.json b/packages/davinci-client/package.json index d91b800251e..1ec05d73433 100644 --- a/packages/davinci-client/package.json +++ b/packages/davinci-client/package.json @@ -30,6 +30,7 @@ "@forgerock/sdk-logger": "workspace:*", "@forgerock/sdk-oidc": "workspace:*", "@forgerock/sdk-request-middleware": "workspace:*", + "@forgerock/sdk-store": "workspace:*", "@forgerock/sdk-types": "workspace:*", "@forgerock/sdk-utilities": "workspace:*", "@forgerock/storage": "workspace:*", diff --git a/packages/davinci-client/src/lib/client.store.effects.ts b/packages/davinci-client/src/lib/client.store.effects.ts index 5828de19d70..a019ced5635 100644 --- a/packages/davinci-client/src/lib/client.store.effects.ts +++ b/packages/davinci-client/src/lib/client.store.effects.ts @@ -5,20 +5,45 @@ * of the MIT license. See the LICENSE file for details. */ +import { createSdkStore, injectClient } from '@forgerock/sdk-store'; import { Micro } from 'effect'; import { createInternalError, isInternalError } from './client.store.utils.js'; +import { configSlice } from './config.slice.js'; import { davinciApi } from './davinci.api.js'; import { nodeSlice } from './node.slice.js'; import type { logger as loggerFn } from '@forgerock/sdk-logger'; +import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore, SdkStoreHandle } from '@forgerock/sdk-store'; import type { FetchBaseQueryError } from '@reduxjs/toolkit/query/react'; import type { SerializedError } from '@reduxjs/toolkit/react'; -import type { ClientStore, RootState } from './client.store.utils.js'; import type { InternalErrorResponse, PollingStatus } from './client.types.js'; import type { PollingCollector } from './collector.types.js'; +/** The Redux store exposed to DaVinci effect helpers. */ +export type DavinciStore = SdkStoreHandle['store']; +import type { RootState } from './davinci.state.js'; + +/** Creates or attaches the store backing a DaVinci client. */ +export function createClientStore({ + requestMiddleware, + logger, + store, +}: { + requestMiddleware?: RequestMiddleware[]; + logger?: ReturnType; + store?: SdkStore; +}): SdkStoreHandle { + return injectClient(store ?? createSdkStore(), { + api: davinciApi, + slices: [configSlice, nodeSlice], + requestMiddleware, + logger, + }); +} + /** * Shape returned by RTK Query's dispatch for the poll endpoint. */ @@ -239,7 +264,7 @@ function challengePollingµ({ }: { collector: PollingCollector; challenge: string; - store: ReturnType; + store: DavinciStore; log: ReturnType; }): Micro.Micro { const maxRetries = collector.output.config.pollRetries ?? 60; @@ -295,7 +320,7 @@ export function pollingµ({ }: { mode: PollingMode; collector: PollingCollector; - store: ReturnType; + store: DavinciStore; log: ReturnType; }): Micro.Micro { if (mode._tag === 'challenge') { diff --git a/packages/davinci-client/src/lib/client.store.test.ts b/packages/davinci-client/src/lib/client.store.test.ts index ff1892b9843..a244617a930 100644 --- a/packages/davinci-client/src/lib/client.store.test.ts +++ b/packages/davinci-client/src/lib/client.store.test.ts @@ -124,6 +124,7 @@ describe('davinci client — cache', () => { describe('cache.getLatestResponse()', () => { it('returns a state_error when no flow has been started (no cache key)', async () => { const client = await davinci({ config: mockConfig }); + if ('type' in client) throw new Error(`davinci() failed: ${client.error}`); // Node is in start status — cache.key is null before any start() call const result = client.cache.getLatestResponse(); @@ -133,6 +134,7 @@ describe('davinci client — cache', () => { it('returns the raw DaVinci response object — NOT a selector function — after start()', async () => { const client = await davinci({ config: mockConfig }); + if ('type' in client) throw new Error(`davinci() failed: ${client.error}`); await client.start(); const result = client.cache.getLatestResponse(); @@ -151,6 +153,7 @@ describe('davinci client — cache', () => { describe('cache.getResponseWithId()', () => { it('returns an argument_error when called with an empty string', async () => { const client = await davinci({ config: mockConfig }); + if ('type' in client) throw new Error(`davinci() failed: ${client.error}`); const result = client.cache.getResponseWithId(''); @@ -159,6 +162,7 @@ describe('davinci client — cache', () => { it('returns the raw DaVinci response object — NOT a selector function — for a valid request ID', async () => { const client = await davinci({ config: mockConfig }); + if ('type' in client) throw new Error(`davinci() failed: ${client.error}`); await client.start(); const node = client.getNode(); @@ -176,6 +180,7 @@ describe('davinci client — cache', () => { it('returns a state_error for a requestId not present in cache', async () => { const client = await davinci({ config: mockConfig }); + if ('type' in client) throw new Error(`davinci() failed: ${client.error}`); const result = client.cache.getResponseWithId('non-existent-id'); diff --git a/packages/davinci-client/src/lib/client.store.ts b/packages/davinci-client/src/lib/client.store.ts index 1fae4b490c1..8ed2afc4941 100644 --- a/packages/davinci-client/src/lib/client.store.ts +++ b/packages/davinci-client/src/lib/client.store.ts @@ -5,17 +5,17 @@ * of the MIT license. See the LICENSE file for details. */ import { logger as loggerFn } from '@forgerock/sdk-logger'; +import { assertValidStore, getClientForReducerPath, wellknownApi } from '@forgerock/sdk-store'; import { createWellknownError, isGenericError } from '@forgerock/sdk-utilities'; import { createStorage } from '@forgerock/storage'; import { Either, Micro } from 'effect'; import { exitIsFail, exitIsSuccess } from 'effect/Micro'; -import { getPollingModeµ, pollingµ } from './client.store.effects.js'; +import { createClientStore, getPollingModeµ, pollingµ } from './client.store.effects.js'; /** * Import RTK slices and api */ import { - createClientStore, createInternalError, handleUpdateValidateError, isValidCollectorCategory, @@ -26,12 +26,11 @@ import { configSlice } from './config.slice.js'; import { davinciApi } from './davinci.api.js'; import { nodeSlice } from './node.slice.js'; import { returnPasswordPolicyValidator } from './password-policy.rules.js'; -import { wellknownApi } from './wellknown.api.js'; import type { CustomLogger, LogLevel } from '@forgerock/sdk-logger'; import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; -import type { RootState } from './client.store.utils.js'; import type { CollectorValueTypes, InitFlow, @@ -53,6 +52,7 @@ import type { * Import the DaVinciRequest types */ import type { DaVinciConfig } from './config.types.js'; +import type { RootState } from './davinci.state.js'; import type { DaVinciAction, DaVinciRequest, @@ -73,6 +73,7 @@ export async function davinci({ config, requestMiddleware, logger, + store: sharedStore, }: { config: DaVinciConfig; requestMiddleware?: RequestMiddleware[]; @@ -80,16 +81,30 @@ export async function davinci({ level: LogLevel; custom?: CustomLogger; }; + /** + * An existing SDK store to attach to, so discovery caching and state are + * shared with another client. Omit to create a store for this client alone. + */ + store?: unknown; }) { const log = loggerFn({ level: logger?.level ?? config.log ?? 'error', custom: logger?.custom, }); - const store = createClientStore({ requestMiddleware, logger: log }); - const serverInfo = createStorage({ - type: 'localStorage', - name: 'serverInfo', - }); + + const storeError = assertValidStore(sharedStore); + if (storeError) return storeError; + + const validStore = sharedStore as SdkStore | undefined; + + if (validStore && getClientForReducerPath(validStore, davinciApi.reducerPath)) { + return { + error: + 'This store already has a DaVinci client attached. Use a separate store per DaVinci client.', + type: 'argument_error' as const, + }; + } + if (!config.serverConfig.wellknown) { const error = new Error( '`wellknown` property is a required as part of the `config.serverConfig`', @@ -104,6 +119,13 @@ export async function davinci({ throw error; } + const handle = createClientStore({ requestMiddleware, logger: log, store: validStore }); + const store = handle.store; + const serverInfo = createStorage({ + type: 'localStorage', + name: 'serverInfo', + }); + const { data: openIdResponse, error: fetchError } = await store.dispatch( wellknownApi.endpoints.configuration.initiate(config.serverConfig.wellknown), ); @@ -117,6 +139,8 @@ export async function davinci({ store.dispatch(configSlice.actions.set({ ...config, wellknownResponse: openIdResponse })); return { + /** Pass to another SDK client's `store` option to share this store. */ + store: handle as SdkStore, // Pass store methods to the client subscribe: store.subscribe, diff --git a/packages/davinci-client/src/lib/client.store.utils.ts b/packages/davinci-client/src/lib/client.store.utils.ts index 31e4eda11dc..d8eb44625e2 100644 --- a/packages/davinci-client/src/lib/client.store.utils.ts +++ b/packages/davinci-client/src/lib/client.store.utils.ts @@ -4,16 +4,8 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import { configureStore } from '@reduxjs/toolkit'; import { Either, Match } from 'effect'; -import { configSlice } from './config.slice.js'; -import { davinciApi } from './davinci.api.js'; -import { nodeSlice } from './node.slice.js'; -import { wellknownApi } from './wellknown.api.js'; - -import type { logger as loggerFn } from '@forgerock/sdk-logger'; -import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; import type { GenericError } from '@forgerock/sdk-types'; import type { @@ -22,59 +14,9 @@ import type { InternalErrorResponse, UpdatableCollectors, } from './client.types.js'; -import type { - CollectorCategory, - Collectors, - ContinueNode, - ErrorNode, - StartNode, - SuccessNode, -} from './node.types.js'; - -export function createClientStore({ - requestMiddleware, - logger, -}: { - requestMiddleware?: RequestMiddleware[]; - logger?: ReturnType; -}) { - return configureStore({ - reducer: { - config: configSlice.reducer, - node: nodeSlice.reducer, - [davinciApi.reducerPath]: davinciApi.reducer, - [wellknownApi.reducerPath]: wellknownApi.reducer, - }, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - thunk: { - extraArgument: { - /** - * This becomes the `api.extra` argument, and will be passed into the - * customer query wrapper for `baseQuery` - */ - requestMiddleware, - logger, - }, - }, - }) - .concat(davinciApi.middleware) - .concat(wellknownApi.middleware), - }); -} - -export type ClientStore = typeof createClientStore; - -export type RootState = ReturnType['getState']>; - -export interface RootStateWithNode< - T extends ErrorNode | ContinueNode | StartNode | SuccessNode, -> extends RootState { - node: T; -} - -export type AppDispatch = ReturnType['dispatch']>; +import type { CollectorCategory, Collectors } from './node.types.js'; +/** The inner Redux store type — used by effects that need dispatch/getState. */ /** * @function createInternalError * @description - Creates an InternalErrorResponse object diff --git a/packages/davinci-client/src/lib/davinci.api.ts b/packages/davinci-client/src/lib/davinci.api.ts index 0eabf2419f8..e66ec9a0809 100644 --- a/packages/davinci-client/src/lib/davinci.api.ts +++ b/packages/davinci-client/src/lib/davinci.api.ts @@ -8,16 +8,17 @@ * Import the RTK Query library from Redux Toolkit * @see https://redux-toolkit.js.org/rtk-query/overview */ +import { logger as loggerFn } from '@forgerock/sdk-logger'; import { createAuthorizeUrl } from '@forgerock/sdk-oidc'; /** * Import internal modules */ import { initQuery } from '@forgerock/sdk-request-middleware'; +import { clientExtra } from '@forgerock/sdk-store'; import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; import { handleResponse, transformActionRequest, transformSubmitRequest } from './davinci.utils.js'; -import type { logger as loggerFn } from '@forgerock/sdk-logger'; import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; import type { GenericError } from '@forgerock/sdk-types'; import type { @@ -28,11 +29,11 @@ import type { } from '@reduxjs/toolkit/query'; import type { StartNode } from '../types.js'; +import type { FidoAuthenticationCollector, FidoRegistrationCollector } from './collector.types.js'; /** * Import the DaVinci types */ -import type { RootStateWithNode } from './client.store.utils.js'; -import type { FidoAuthenticationCollector, FidoRegistrationCollector } from './collector.types.js'; +import type { RootStateWithNode } from './davinci.state.js'; import type { DaVinciCacheEntry, OutgoingQueryParams, @@ -45,9 +46,34 @@ type BaseQueryResponse = Promise< QueryReturnValue >; +const DAVINCI_REDUCER_PATH = 'davinci'; + +/** + * This client's private slot on the store's `extraArgument`. + * + * Both fields are optional because a shared store may not have had a davinci + * slot registered yet; `davinciExtra` substitutes safe defaults so an endpoint + * can never fail on a missing slot. + */ interface Extras { - requestMiddleware: RequestMiddleware[]; - logger: ReturnType; + requestMiddleware?: RequestMiddleware[]; + logger?: ReturnType; +} + +/** Creates an error-level fallback so a missing slot degrades safely, never crashes. */ +const createFallbackLogger = () => loggerFn({ level: 'error' }); + +/** + * Resolves this client's own middleware and logger. + * + * Reads only the `davinci` slot — never a store-wide value, which on a shared + * store would belong to whichever client created it. + */ +function davinciExtra(extra: unknown): Required { + return clientExtra(extra, DAVINCI_REDUCER_PATH, { + requestMiddleware: [], + logger: createFallbackLogger(), + }); } /** @@ -55,7 +81,7 @@ interface Extras { @@ -79,9 +105,9 @@ export const davinciApi = createApi({ const requestBody = transformActionRequest( state.node, params.action, - (api.extra as Extras).logger, + davinciExtra(api.extra).logger, ); - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = davinciExtra(api.extra); let href = ''; @@ -124,7 +150,7 @@ export const davinciApi = createApi({ * parameters are pre-typed from the library. */ async onQueryStarted(_, api) { - const logger = (api.extra as Extras).logger; + const { logger } = davinciExtra(api.extra); let response; try { @@ -158,7 +184,7 @@ export const davinciApi = createApi({ async queryFn(body, api, __, baseQuery) { const state = api.getState() as RootStateWithNode; const links = state.node.server._links; - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = davinciExtra(api.extra); let requestBody; let href = ''; @@ -235,7 +261,7 @@ export const davinciApi = createApi({ * parameters are pre-typed from the library. */ async onQueryStarted(_, api) { - const logger = (api.extra as Extras).logger; + const { logger } = davinciExtra(api.extra); let response; try { @@ -268,7 +294,7 @@ export const davinciApi = createApi({ * @method queryFn - This is just a wrapper around the fetch call */ async queryFn(options, api, __, baseQuery) { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = davinciExtra(api.extra); const state = api.getState() as RootStateWithNode; if (!state) { @@ -350,7 +376,7 @@ export const davinciApi = createApi({ * parameters are pre-typed from the library. */ async onQueryStarted(_, api) { - const logger = (api.extra as Extras).logger; + const { logger } = davinciExtra(api.extra); let response; try { @@ -379,7 +405,7 @@ export const davinciApi = createApi({ */ resume: builder.query({ async queryFn({ serverInfo, continueToken }, api, _c, baseQuery) { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = davinciExtra(api.extra); const links = serverInfo._links; if (!continueToken) { @@ -428,7 +454,7 @@ export const davinciApi = createApi({ return response; }, async onQueryStarted(_, api) { - const logger = (api.extra as Extras).logger; + const { logger } = davinciExtra(api.extra); let response; try { @@ -462,7 +488,7 @@ export const davinciApi = createApi({ */ poll: builder.mutation({ async queryFn({ endpoint, interactionId }, api, _c, baseQuery) { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = davinciExtra(api.extra); const request: FetchArgs = { url: endpoint, diff --git a/packages/davinci-client/src/lib/davinci.state.ts b/packages/davinci-client/src/lib/davinci.state.ts new file mode 100644 index 00000000000..096805badf0 --- /dev/null +++ b/packages/davinci-client/src/lib/davinci.state.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { wellknownApi } from '@forgerock/sdk-store'; +import { combineSlices } from '@reduxjs/toolkit'; + +import { configSlice } from './config.slice.js'; +import { davinciApi } from './davinci.api.js'; +import { nodeSlice } from './node.slice.js'; + +import type { ContinueNode, ErrorNode, StartNode, SuccessNode } from './node.types.js'; + +/** + * The canonical description of the state this client contributes. + * + * Isolated into its own module to prevent a circular dependency: + * `node.reducer.ts` → `client.store.utils.ts` → `node.slice.ts` would cycle. + * Nothing that `node.reducer.ts` imports should import from this file. + */ +export const rootReducer = combineSlices(configSlice, nodeSlice, davinciApi, wellknownApi); + +export type RootState = ReturnType; + +export interface RootStateWithNode< + T extends ErrorNode | ContinueNode | StartNode | SuccessNode, +> extends RootState { + node: T; +} diff --git a/packages/davinci-client/src/lib/store-shape.test.ts b/packages/davinci-client/src/lib/store-shape.test.ts new file mode 100644 index 00000000000..fd7c8f1c820 --- /dev/null +++ b/packages/davinci-client/src/lib/store-shape.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { describe, expect, it } from 'vitest'; + +import { createClientStore } from './client.store.effects.js'; + +/** + * `combineSlices` keys each reducer off `slice.reducerPath ?? slice.name`, where + * the previous `configureStore({ reducer: { ... } })` form spelled the keys out + * literally. That makes the published state shape an implicit consequence of + * slice metadata: renaming `nodeSlice.name` would silently reshape the store. + * + * These assertions pin the shape so such a rename fails loudly here instead of + * in a consumer's selectors. + */ +describe('davinci store shape', () => { + it('exposes exactly the expected top-level state keys', () => { + // Arrange + const { store } = createClientStore({}); + + // Act + const keys = Object.keys(store.getState()).sort(); + + // Assert + expect(keys).toEqual(['config', 'davinci', 'node', 'wellknown']); + }); + + it('registers this client\u2019s slot on the store extra, keyed by reducerPath', async () => { + // Arrange + const { store } = createClientStore({}); + let observed: unknown; + + // Act — a thunk is the supported way to observe extraArgument + await store.dispatch(((_dispatch: unknown, _getState: unknown, extra: unknown) => { + observed = extra; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as any); + + // Assert + expect(observed).toHaveProperty('clients.davinci'); + }); +}); diff --git a/packages/davinci-client/src/lib/wellknown.api.ts b/packages/davinci-client/src/lib/wellknown.api.ts deleted file mode 100644 index 882a66bcc38..00000000000 --- a/packages/davinci-client/src/lib/wellknown.api.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - -import { initWellknownQuery } from '@forgerock/sdk-oidc'; -import { createSelector } from '@reduxjs/toolkit'; -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; - -import type { WellknownResponse } from '@forgerock/sdk-types'; -import type { - FetchBaseQueryError, - FetchBaseQueryMeta, - QueryReturnValue, -} from '@reduxjs/toolkit/query'; - -/** - * RTK Query API for well-known endpoint discovery. - * - * Uses the `initWellknownQuery` builder pattern from `@forgerock/sdk-oidc`. - * The builder constructs the request and validates the response; - * `fetchBaseQuery` handles the HTTP transport through RTK Query's pipeline. - */ -export const wellknownApi = createApi({ - reducerPath: 'wellknown', - baseQuery: fetchBaseQuery(), - endpoints: (builder) => ({ - configuration: builder.query({ - queryFn: async (url, _api, _extra, baseQuery) => { - const result = await initWellknownQuery(url).applyQuery(async (req) => { - const queryResult = await baseQuery(req); - return queryResult as QueryReturnValue; - }); - return result as QueryReturnValue< - WellknownResponse, - FetchBaseQueryError, - FetchBaseQueryMeta - >; - }, - }), - }), -}); - -/** - * Creates a memoized selector for cached well-known data. - * - * @param wellknownUrl - The well-known endpoint URL used as the cache key - * @returns A memoized selector that extracts the WellknownResponse from state, or undefined if not yet fetched - */ -export function createWellknownSelector(wellknownUrl: string) { - return createSelector( - wellknownApi.endpoints.configuration.select(wellknownUrl), - (result) => result?.data, - ); -} diff --git a/packages/davinci-client/tsconfig.json b/packages/davinci-client/tsconfig.json index 141b4ebf5f8..df687466594 100644 --- a/packages/davinci-client/tsconfig.json +++ b/packages/davinci-client/tsconfig.json @@ -23,6 +23,9 @@ { "path": "../sdk-effects/sdk-request-middleware" }, + { + "path": "../sdk-effects/store" + }, { "path": "../sdk-effects/oidc" }, diff --git a/packages/davinci-client/tsconfig.lib.json b/packages/davinci-client/tsconfig.lib.json index 6c7bbdefeff..6db7b33e75b 100644 --- a/packages/davinci-client/tsconfig.lib.json +++ b/packages/davinci-client/tsconfig.lib.json @@ -48,6 +48,9 @@ }, { "path": "../sdk-effects/logger/tsconfig.lib.json" + }, + { + "path": "../sdk-effects/store/tsconfig.lib.json" } ] } diff --git a/packages/device-client/api-report/device-client.api.md b/packages/device-client/api-report/device-client.api.md index 77ef261064b..797330f24de 100644 --- a/packages/device-client/api-report/device-client.api.md +++ b/packages/device-client/api-report/device-client.api.md @@ -1,527 +1,499 @@ -## API Report File for "@forgerock/device-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import type { LegacyConfigOptions } from '@forgerock/sdk-types'; - -// @public (undocumented) -export type Bluetooth = { - supported: true; -}; - -// @public (undocumented) -export type BoundDeviceQuery = GetBoundDevicesQuery & { - device: Device; -}; - -// @public (undocumented) -export type Browser = { - userAgent: string; -}; - -// @public (undocumented) -export type ConfigOptions = LegacyConfigOptions; - -// @public (undocumented) -export type DeleteDeviceQuery = { - realm?: string; - userId: string; - device: PushDevice; -}; - -// @public (undocumented) -export type DeletedOathDevice = { - _id: string; - _rev: string; - uuid: string; - recoveryCodes: string[]; - createdDate: number; - lastAccessDate: number; - sharedSecret: string; - deviceName: string; - lastLogin: number; - counter: number; - checksumDigit: boolean; - truncationOffset: number; - clockDriftSeconds: number; -}; - -// @public (undocumented) -export interface DeletedPushDevice { - // (undocumented) - communicationId: string; - // (undocumented) - communicationType: string; - // (undocumented) - createdDate: number; - // (undocumented) - deviceId: string; - // (undocumented) - deviceMechanismUID: string; - // (undocumented) - deviceName: string; - // (undocumented) - deviceType: string; - // (undocumented) - _id: string; - // (undocumented) - issuer: string; - // (undocumented) - lastAccessDate: number; - // (undocumented) - recoveryCodes: string[]; - // (undocumented) - _rev: string; - // (undocumented) - sharedSecret: string; - // (undocumented) - uuid: string; -} - -// @public (undocumented) -export type DeleteOathQuery = { - realm?: string; - userId: string; - uuid: string; -}; - -// @public (undocumented) -export type Device = { - _id: string; - _rev: string; - createdDate: number; - lastAccessDate: number; - deviceId: string; - deviceName: string; - uuid: string; - recoveryCodes: string[]; - key: { - kty: string; - kid: string; - use: string; - alg: string; - n: string; - e: string; - }; - deviceManagementStatus: boolean; -}; - -// @public (undocumented) -export type DeviceClient = ReturnType; - -// @public -export const deviceClient: (config: LegacyConfigOptions) => { - oath: { - get: (query: RetrieveOathQuery) => Promise< - | OathDevice[] - | { - error: unknown; - } - >; - delete: ( - query: RetrieveOathQuery & { - device: OathDevice; - }, - ) => Promise; - }; - push: { - get: (query: PushDeviceQuery) => Promise< - | PushDevice[] - | { - error: unknown; - } - >; - delete: (query: DeleteDeviceQuery) => Promise; - }; - webAuthn: { - get: (query: WebAuthnQuery) => Promise< - | WebAuthnDevice[] - | { - error: unknown; - } - >; - update: ( - query: WebAuthnQuery & { - device: WebAuthnDevice; - }, - ) => Promise< - | UpdatedWebAuthnDevice - | { - error: unknown; - } - >; - delete: ( - query: WebAuthnQuery & { - device: WebAuthnDevice | UpdatedWebAuthnDevice; - }, - ) => Promise; - }; - bound: { - get: (query: GetBoundDevicesQuery) => Promise< - | Device[] - | { - error: unknown; - } - >; - delete: (query: BoundDeviceQuery) => Promise; - update: (query: BoundDeviceQuery) => Promise< - | Device - | { - error: unknown; - } - >; - }; - profile: { - get: (query: GetProfileDevices) => Promise< - | ProfileDevice[] - | { - error: unknown; - } - >; - update: (query: ProfileDevicesQuery) => Promise< - | ProfileDevice - | { - error: unknown; - } - >; - delete: (query: ProfileDevicesQuery) => Promise; - }; -}; - -// @public (undocumented) -export type DeviceInfo = { - _id: string; - _rev: string; - identifier: string; - metadata: { - platform: { - platform: string; - version: number; - device: string; - deviceName: string; - model: string; - brand: string; - locale: string; - timeZone: string; - jailBreakScore: number; - }; - hardware: { - hardware: string; - manufacturer: string; - storage: number; - memory: number; - cpu: number; - display: { - width: number; - height: number; - orientation: number; - }; - camera: { - numberOfCameras: number; - }; - }; - browser: { - userAgent: string; - }; - bluetooth: { - supported: boolean; - }; - network: { - connected: boolean; - }; - telephony: { - networkCountryIso: string; - carrierName: string; - }; - }; - lastSelectedDate: number; - alias: string; - recoveryCodes: string[]; -}; - -// @public (undocumented) -export type DeviceInfoResponse = { - result: DeviceInfo[]; - resultCount: number; - pagedResultsCookie: null; - totalPagedResultsPolicy: string; - totalPagedResults: -1; - remainingPagedResults: -1; -}; - -// @public (undocumented) -export type DeviceMetadata = { - platform: string; - version: 34; - device: string; - deviceName: string; - model: string; - brand: string; - locale: string; - timeZone: string; - jailBreakScore: 0; -}; - -// @public (undocumented) -export type DeviceProfile = { - _id: string; - _rev: string; - identifier: string; - metadata: Metadata; - lastSelectedDate: number; - alias: 'Test3'; - recoveryCodes: []; -}; - -// @public (undocumented) -export type DeviceResponse = { - result: Device[]; - resultCount: number; - pagedResultsCookie: null; - totalPagedResultsPolicy: string; - totalPagedResults: -1; - remainingPagedResults: -1; -}; - -// @public (undocumented) -export type GetBoundDevicesQuery = { - userId: string; - realm?: string; -}; - -// @public (undocumented) -export interface GetProfileDevices { - // (undocumented) - realm?: string; - // (undocumented) - userId: string; -} - -// @public (undocumented) -export type Hardware = { - hardware: string; - manufacturer: string; - storage: 5939; - memory: 2981; - cpu: 4; - display: { - width: 1440; - height: 2678; - orientation: 1; - }; - camera: { - numberOfCameras: 2; - }; -}; - -// @public (undocumented) -export type Metadata = { - platform: DeviceMetadata; - hardware: Hardware; - browser: Browser; - bluetooth: Bluetooth; - network: Network; - telephony: Telephony; -}; - -// @public (undocumented) -export type Network = { - connected: true; -}; - -// @public (undocumented) -export type OathDevice = { - _id: string; - deviceManagementStatus: boolean; - deviceName: string; - uuid: string; - createdDate: number; - lastAccessDate: number; - _rev: string; -}; - -// @public (undocumented) -export type OathResponse = { - pagedResultsCookie: string | null; - remainingPagedResults: number; - resultCount: number; - totalPagedResults: number; - totalPagedResultsPolicy: string; - result: OathDevice[]; -}; - -// @public (undocumented) -export type ProfileDevice = { - _id: string; - _rev: string; - identifier: string; - metadata: { - platform: { - platform: string; - version: number; - device: string; - deviceName: string; - model: string; - brand: string; - locale: string; - timeZone: string; - jailBreakScore: number; - }; - hardware: { - hardware: string; - manufacturer: string; - storage: number; - memory: number; - cpu: number; - display: { - width: number; - height: number; - orientation: number; - }; - camera: { - numberOfCameras: number; - }; - }; - browser: { - userAgent: string; - }; - bluetooth: { - supported: boolean; - }; - network: { - connected: boolean; - }; - telephony: { - networkCountryIso: string; - carrierName: string; - }; - }; - lastSelectedDate: number; - alias: string; - recoveryCodes: string[]; -}; - -// @public (undocumented) -export interface ProfileDevicesQuery extends GetProfileDevices { - // (undocumented) - device: ProfileDevice; -} - -// @public (undocumented) -export type PushDevice = { - _id: string; - _rev: string; - createdDate: number; - lastAccessDate: number; - deviceName: string; - uuid: string; - deviceManagementStatus: boolean; -}; - -// @public (undocumented) -export type PushDeviceBody = { - id: string; - deviceName: string; - uuid: string; - createdDate: string; - lastAccessDate: string; -}; - -// @public (undocumented) -export type PushDeviceQuery = { - realm?: string; - userId: string; -}; - -// @public (undocumented) -export type RetrieveOathQuery = { - realm?: string; - userId: string; -}; - -// @public (undocumented) -export type Telephony = { - networkCountryIso: 'us'; - carrierName: 'T-Mobile'; -}; - -// @public (undocumented) -export type UpdatedWebAuthnDevice = { - _id: string; - _rev: string; - uuid: string; - recoveryCodes: string[]; - createdDate: number; - lastAccessDate: number; - credentialId: string; - algorithm: string; - deviceName: string; - signCount: number; - key: { - kty: string; - x: string; - y: string; - crv: string; - }; -}; - -// @public (undocumented) -export type WebAuthnBody = { - id: string; - deviceName: string; - uuid: string; - credentialId: string; - createdDate: number; - lastAccessDate: number; -}; - -// @public (undocumented) -export type WebAuthnCredential = { - _id: string; - _rev: string; - uuid: string; - recoveryCodes: string[]; - createdDate: number; - lastAccessDate: number; - credentialId: string; - algorithm: string; - deviceName: string; - key: { - kty: string; - x: string; - y: string; - crv: string; - }; -}; - -// @public (undocumented) -export type WebAuthnDevice = { - _id: string; - _rev: string; - createdDate: number; - lastAccessDate: number; - credentialId: string; - deviceName: string; - uuid: string; - deviceManagementStatus: boolean; -}; - -// @public (undocumented) -export type WebAuthnQuery = { - realm?: string; - userId: string; -}; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/device-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { LegacyConfigOptions } from '@forgerock/sdk-types'; + +// @public (undocumented) +export type Bluetooth = { + supported: true; +}; + +// @public (undocumented) +export type BoundDeviceQuery = GetBoundDevicesQuery & { + device: Device; +}; + +// @public (undocumented) +export type Browser = { + userAgent: string; +}; + +// @public (undocumented) +export type ConfigOptions = LegacyConfigOptions; + +// @public (undocumented) +export type DeleteDeviceQuery = { + realm?: string; + userId: string; + device: PushDevice; +}; + +// @public (undocumented) +export type DeletedOathDevice = { + _id: string; + _rev: string; + uuid: string; + recoveryCodes: string[]; + createdDate: number; + lastAccessDate: number; + sharedSecret: string; + deviceName: string; + lastLogin: number; + counter: number; + checksumDigit: boolean; + truncationOffset: number; + clockDriftSeconds: number; +}; + +// @public (undocumented) +export interface DeletedPushDevice { + // (undocumented) + communicationId: string; + // (undocumented) + communicationType: string; + // (undocumented) + createdDate: number; + // (undocumented) + deviceId: string; + // (undocumented) + deviceMechanismUID: string; + // (undocumented) + deviceName: string; + // (undocumented) + deviceType: string; + // (undocumented) + _id: string; + // (undocumented) + issuer: string; + // (undocumented) + lastAccessDate: number; + // (undocumented) + recoveryCodes: string[]; + // (undocumented) + _rev: string; + // (undocumented) + sharedSecret: string; + // (undocumented) + uuid: string; +} + +// @public (undocumented) +export type DeleteOathQuery = { + realm?: string; + userId: string; + uuid: string; +}; + +// @public (undocumented) +export type Device = { + _id: string; + _rev: string; + createdDate: number; + lastAccessDate: number; + deviceId: string; + deviceName: string; + uuid: string; + recoveryCodes: string[]; + key: { + kty: string; + kid: string; + use: string; + alg: string; + n: string; + e: string; + }; + deviceManagementStatus: boolean; +}; + +// @public (undocumented) +export type DeviceClient = ReturnType; + +// @public +export const deviceClient: (config: LegacyConfigOptions) => { + oath: { + get: (query: RetrieveOathQuery) => Promise; + delete: (query: RetrieveOathQuery & { + device: OathDevice; + }) => Promise; + }; + push: { + get: (query: PushDeviceQuery) => Promise; + delete: (query: DeleteDeviceQuery) => Promise; + }; + webAuthn: { + get: (query: WebAuthnQuery) => Promise; + update: (query: WebAuthnQuery & { + device: WebAuthnDevice; + }) => Promise; + delete: (query: WebAuthnQuery & { + device: WebAuthnDevice | UpdatedWebAuthnDevice; + }) => Promise; + }; + bound: { + get: (query: GetBoundDevicesQuery) => Promise; + delete: (query: BoundDeviceQuery) => Promise; + update: (query: BoundDeviceQuery) => Promise; + }; + profile: { + get: (query: GetProfileDevices) => Promise; + update: (query: ProfileDevicesQuery) => Promise; + delete: (query: ProfileDevicesQuery) => Promise; + }; +}; + +// @public (undocumented) +export type DeviceInfo = { + _id: string; + _rev: string; + identifier: string; + metadata: { + platform: { + platform: string; + version: number; + device: string; + deviceName: string; + model: string; + brand: string; + locale: string; + timeZone: string; + jailBreakScore: number; + }; + hardware: { + hardware: string; + manufacturer: string; + storage: number; + memory: number; + cpu: number; + display: { + width: number; + height: number; + orientation: number; + }; + camera: { + numberOfCameras: number; + }; + }; + browser: { + userAgent: string; + }; + bluetooth: { + supported: boolean; + }; + network: { + connected: boolean; + }; + telephony: { + networkCountryIso: string; + carrierName: string; + }; + }; + lastSelectedDate: number; + alias: string; + recoveryCodes: string[]; +}; + +// @public (undocumented) +export type DeviceInfoResponse = { + result: DeviceInfo[]; + resultCount: number; + pagedResultsCookie: null; + totalPagedResultsPolicy: string; + totalPagedResults: -1; + remainingPagedResults: -1; +}; + +// @public (undocumented) +export type DeviceMetadata = { + platform: string; + version: 34; + device: string; + deviceName: string; + model: string; + brand: string; + locale: string; + timeZone: string; + jailBreakScore: 0; +}; + +// @public (undocumented) +export type DeviceProfile = { + _id: string; + _rev: string; + identifier: string; + metadata: Metadata; + lastSelectedDate: number; + alias: 'Test3'; + recoveryCodes: []; +}; + +// @public (undocumented) +export type DeviceResponse = { + result: Device[]; + resultCount: number; + pagedResultsCookie: null; + totalPagedResultsPolicy: string; + totalPagedResults: -1; + remainingPagedResults: -1; +}; + +// @public (undocumented) +export type GetBoundDevicesQuery = { + userId: string; + realm?: string; +}; + +// @public (undocumented) +export interface GetProfileDevices { + // (undocumented) + realm?: string; + // (undocumented) + userId: string; +} + +// @public (undocumented) +export type Hardware = { + hardware: string; + manufacturer: string; + storage: 5939; + memory: 2981; + cpu: 4; + display: { + width: 1440; + height: 2678; + orientation: 1; + }; + camera: { + numberOfCameras: 2; + }; +}; + +// @public (undocumented) +export type Metadata = { + platform: DeviceMetadata; + hardware: Hardware; + browser: Browser; + bluetooth: Bluetooth; + network: Network; + telephony: Telephony; +}; + +// @public (undocumented) +export type Network = { + connected: true; +}; + +// @public (undocumented) +export type OathDevice = { + _id: string; + deviceManagementStatus: boolean; + deviceName: string; + uuid: string; + createdDate: number; + lastAccessDate: number; + _rev: string; +}; + +// @public (undocumented) +export type OathResponse = { + pagedResultsCookie: string | null; + remainingPagedResults: number; + resultCount: number; + totalPagedResults: number; + totalPagedResultsPolicy: string; + result: OathDevice[]; +}; + +// @public (undocumented) +export type ProfileDevice = { + _id: string; + _rev: string; + identifier: string; + metadata: { + platform: { + platform: string; + version: number; + device: string; + deviceName: string; + model: string; + brand: string; + locale: string; + timeZone: string; + jailBreakScore: number; + }; + hardware: { + hardware: string; + manufacturer: string; + storage: number; + memory: number; + cpu: number; + display: { + width: number; + height: number; + orientation: number; + }; + camera: { + numberOfCameras: number; + }; + }; + browser: { + userAgent: string; + }; + bluetooth: { + supported: boolean; + }; + network: { + connected: boolean; + }; + telephony: { + networkCountryIso: string; + carrierName: string; + }; + }; + lastSelectedDate: number; + alias: string; + recoveryCodes: string[]; +}; + +// @public (undocumented) +export interface ProfileDevicesQuery extends GetProfileDevices { + // (undocumented) + device: ProfileDevice; +} + +// @public (undocumented) +export type PushDevice = { + _id: string; + _rev: string; + createdDate: number; + lastAccessDate: number; + deviceName: string; + uuid: string; + deviceManagementStatus: boolean; +}; + +// @public (undocumented) +export type PushDeviceBody = { + id: string; + deviceName: string; + uuid: string; + createdDate: string; + lastAccessDate: string; +}; + +// @public (undocumented) +export type PushDeviceQuery = { + realm?: string; + userId: string; +}; + +// @public (undocumented) +export type RetrieveOathQuery = { + realm?: string; + userId: string; +}; + +// @public (undocumented) +export type Telephony = { + networkCountryIso: 'us'; + carrierName: 'T-Mobile'; +}; + +// @public (undocumented) +export type UpdatedWebAuthnDevice = { + _id: string; + _rev: string; + uuid: string; + recoveryCodes: string[]; + createdDate: number; + lastAccessDate: number; + credentialId: string; + algorithm: string; + deviceName: string; + signCount: number; + key: { + kty: string; + x: string; + y: string; + crv: string; + }; +}; + +// @public (undocumented) +export type WebAuthnBody = { + id: string; + deviceName: string; + uuid: string; + credentialId: string; + createdDate: number; + lastAccessDate: number; +}; + +// @public (undocumented) +export type WebAuthnCredential = { + _id: string; + _rev: string; + uuid: string; + recoveryCodes: string[]; + createdDate: number; + lastAccessDate: number; + credentialId: string; + algorithm: string; + deviceName: string; + key: { + kty: string; + x: string; + y: string; + crv: string; + }; +}; + +// @public (undocumented) +export type WebAuthnDevice = { + _id: string; + _rev: string; + createdDate: number; + lastAccessDate: number; + credentialId: string; + deviceName: string; + uuid: string; + deviceManagementStatus: boolean; +}; + +// @public (undocumented) +export type WebAuthnQuery = { + realm?: string; + userId: string; +}; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/device-client/api-report/device-client.types.api.md b/packages/device-client/api-report/device-client.types.api.md index 77ef261064b..797330f24de 100644 --- a/packages/device-client/api-report/device-client.types.api.md +++ b/packages/device-client/api-report/device-client.types.api.md @@ -1,527 +1,499 @@ -## API Report File for "@forgerock/device-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import type { LegacyConfigOptions } from '@forgerock/sdk-types'; - -// @public (undocumented) -export type Bluetooth = { - supported: true; -}; - -// @public (undocumented) -export type BoundDeviceQuery = GetBoundDevicesQuery & { - device: Device; -}; - -// @public (undocumented) -export type Browser = { - userAgent: string; -}; - -// @public (undocumented) -export type ConfigOptions = LegacyConfigOptions; - -// @public (undocumented) -export type DeleteDeviceQuery = { - realm?: string; - userId: string; - device: PushDevice; -}; - -// @public (undocumented) -export type DeletedOathDevice = { - _id: string; - _rev: string; - uuid: string; - recoveryCodes: string[]; - createdDate: number; - lastAccessDate: number; - sharedSecret: string; - deviceName: string; - lastLogin: number; - counter: number; - checksumDigit: boolean; - truncationOffset: number; - clockDriftSeconds: number; -}; - -// @public (undocumented) -export interface DeletedPushDevice { - // (undocumented) - communicationId: string; - // (undocumented) - communicationType: string; - // (undocumented) - createdDate: number; - // (undocumented) - deviceId: string; - // (undocumented) - deviceMechanismUID: string; - // (undocumented) - deviceName: string; - // (undocumented) - deviceType: string; - // (undocumented) - _id: string; - // (undocumented) - issuer: string; - // (undocumented) - lastAccessDate: number; - // (undocumented) - recoveryCodes: string[]; - // (undocumented) - _rev: string; - // (undocumented) - sharedSecret: string; - // (undocumented) - uuid: string; -} - -// @public (undocumented) -export type DeleteOathQuery = { - realm?: string; - userId: string; - uuid: string; -}; - -// @public (undocumented) -export type Device = { - _id: string; - _rev: string; - createdDate: number; - lastAccessDate: number; - deviceId: string; - deviceName: string; - uuid: string; - recoveryCodes: string[]; - key: { - kty: string; - kid: string; - use: string; - alg: string; - n: string; - e: string; - }; - deviceManagementStatus: boolean; -}; - -// @public (undocumented) -export type DeviceClient = ReturnType; - -// @public -export const deviceClient: (config: LegacyConfigOptions) => { - oath: { - get: (query: RetrieveOathQuery) => Promise< - | OathDevice[] - | { - error: unknown; - } - >; - delete: ( - query: RetrieveOathQuery & { - device: OathDevice; - }, - ) => Promise; - }; - push: { - get: (query: PushDeviceQuery) => Promise< - | PushDevice[] - | { - error: unknown; - } - >; - delete: (query: DeleteDeviceQuery) => Promise; - }; - webAuthn: { - get: (query: WebAuthnQuery) => Promise< - | WebAuthnDevice[] - | { - error: unknown; - } - >; - update: ( - query: WebAuthnQuery & { - device: WebAuthnDevice; - }, - ) => Promise< - | UpdatedWebAuthnDevice - | { - error: unknown; - } - >; - delete: ( - query: WebAuthnQuery & { - device: WebAuthnDevice | UpdatedWebAuthnDevice; - }, - ) => Promise; - }; - bound: { - get: (query: GetBoundDevicesQuery) => Promise< - | Device[] - | { - error: unknown; - } - >; - delete: (query: BoundDeviceQuery) => Promise; - update: (query: BoundDeviceQuery) => Promise< - | Device - | { - error: unknown; - } - >; - }; - profile: { - get: (query: GetProfileDevices) => Promise< - | ProfileDevice[] - | { - error: unknown; - } - >; - update: (query: ProfileDevicesQuery) => Promise< - | ProfileDevice - | { - error: unknown; - } - >; - delete: (query: ProfileDevicesQuery) => Promise; - }; -}; - -// @public (undocumented) -export type DeviceInfo = { - _id: string; - _rev: string; - identifier: string; - metadata: { - platform: { - platform: string; - version: number; - device: string; - deviceName: string; - model: string; - brand: string; - locale: string; - timeZone: string; - jailBreakScore: number; - }; - hardware: { - hardware: string; - manufacturer: string; - storage: number; - memory: number; - cpu: number; - display: { - width: number; - height: number; - orientation: number; - }; - camera: { - numberOfCameras: number; - }; - }; - browser: { - userAgent: string; - }; - bluetooth: { - supported: boolean; - }; - network: { - connected: boolean; - }; - telephony: { - networkCountryIso: string; - carrierName: string; - }; - }; - lastSelectedDate: number; - alias: string; - recoveryCodes: string[]; -}; - -// @public (undocumented) -export type DeviceInfoResponse = { - result: DeviceInfo[]; - resultCount: number; - pagedResultsCookie: null; - totalPagedResultsPolicy: string; - totalPagedResults: -1; - remainingPagedResults: -1; -}; - -// @public (undocumented) -export type DeviceMetadata = { - platform: string; - version: 34; - device: string; - deviceName: string; - model: string; - brand: string; - locale: string; - timeZone: string; - jailBreakScore: 0; -}; - -// @public (undocumented) -export type DeviceProfile = { - _id: string; - _rev: string; - identifier: string; - metadata: Metadata; - lastSelectedDate: number; - alias: 'Test3'; - recoveryCodes: []; -}; - -// @public (undocumented) -export type DeviceResponse = { - result: Device[]; - resultCount: number; - pagedResultsCookie: null; - totalPagedResultsPolicy: string; - totalPagedResults: -1; - remainingPagedResults: -1; -}; - -// @public (undocumented) -export type GetBoundDevicesQuery = { - userId: string; - realm?: string; -}; - -// @public (undocumented) -export interface GetProfileDevices { - // (undocumented) - realm?: string; - // (undocumented) - userId: string; -} - -// @public (undocumented) -export type Hardware = { - hardware: string; - manufacturer: string; - storage: 5939; - memory: 2981; - cpu: 4; - display: { - width: 1440; - height: 2678; - orientation: 1; - }; - camera: { - numberOfCameras: 2; - }; -}; - -// @public (undocumented) -export type Metadata = { - platform: DeviceMetadata; - hardware: Hardware; - browser: Browser; - bluetooth: Bluetooth; - network: Network; - telephony: Telephony; -}; - -// @public (undocumented) -export type Network = { - connected: true; -}; - -// @public (undocumented) -export type OathDevice = { - _id: string; - deviceManagementStatus: boolean; - deviceName: string; - uuid: string; - createdDate: number; - lastAccessDate: number; - _rev: string; -}; - -// @public (undocumented) -export type OathResponse = { - pagedResultsCookie: string | null; - remainingPagedResults: number; - resultCount: number; - totalPagedResults: number; - totalPagedResultsPolicy: string; - result: OathDevice[]; -}; - -// @public (undocumented) -export type ProfileDevice = { - _id: string; - _rev: string; - identifier: string; - metadata: { - platform: { - platform: string; - version: number; - device: string; - deviceName: string; - model: string; - brand: string; - locale: string; - timeZone: string; - jailBreakScore: number; - }; - hardware: { - hardware: string; - manufacturer: string; - storage: number; - memory: number; - cpu: number; - display: { - width: number; - height: number; - orientation: number; - }; - camera: { - numberOfCameras: number; - }; - }; - browser: { - userAgent: string; - }; - bluetooth: { - supported: boolean; - }; - network: { - connected: boolean; - }; - telephony: { - networkCountryIso: string; - carrierName: string; - }; - }; - lastSelectedDate: number; - alias: string; - recoveryCodes: string[]; -}; - -// @public (undocumented) -export interface ProfileDevicesQuery extends GetProfileDevices { - // (undocumented) - device: ProfileDevice; -} - -// @public (undocumented) -export type PushDevice = { - _id: string; - _rev: string; - createdDate: number; - lastAccessDate: number; - deviceName: string; - uuid: string; - deviceManagementStatus: boolean; -}; - -// @public (undocumented) -export type PushDeviceBody = { - id: string; - deviceName: string; - uuid: string; - createdDate: string; - lastAccessDate: string; -}; - -// @public (undocumented) -export type PushDeviceQuery = { - realm?: string; - userId: string; -}; - -// @public (undocumented) -export type RetrieveOathQuery = { - realm?: string; - userId: string; -}; - -// @public (undocumented) -export type Telephony = { - networkCountryIso: 'us'; - carrierName: 'T-Mobile'; -}; - -// @public (undocumented) -export type UpdatedWebAuthnDevice = { - _id: string; - _rev: string; - uuid: string; - recoveryCodes: string[]; - createdDate: number; - lastAccessDate: number; - credentialId: string; - algorithm: string; - deviceName: string; - signCount: number; - key: { - kty: string; - x: string; - y: string; - crv: string; - }; -}; - -// @public (undocumented) -export type WebAuthnBody = { - id: string; - deviceName: string; - uuid: string; - credentialId: string; - createdDate: number; - lastAccessDate: number; -}; - -// @public (undocumented) -export type WebAuthnCredential = { - _id: string; - _rev: string; - uuid: string; - recoveryCodes: string[]; - createdDate: number; - lastAccessDate: number; - credentialId: string; - algorithm: string; - deviceName: string; - key: { - kty: string; - x: string; - y: string; - crv: string; - }; -}; - -// @public (undocumented) -export type WebAuthnDevice = { - _id: string; - _rev: string; - createdDate: number; - lastAccessDate: number; - credentialId: string; - deviceName: string; - uuid: string; - deviceManagementStatus: boolean; -}; - -// @public (undocumented) -export type WebAuthnQuery = { - realm?: string; - userId: string; -}; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/device-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { LegacyConfigOptions } from '@forgerock/sdk-types'; + +// @public (undocumented) +export type Bluetooth = { + supported: true; +}; + +// @public (undocumented) +export type BoundDeviceQuery = GetBoundDevicesQuery & { + device: Device; +}; + +// @public (undocumented) +export type Browser = { + userAgent: string; +}; + +// @public (undocumented) +export type ConfigOptions = LegacyConfigOptions; + +// @public (undocumented) +export type DeleteDeviceQuery = { + realm?: string; + userId: string; + device: PushDevice; +}; + +// @public (undocumented) +export type DeletedOathDevice = { + _id: string; + _rev: string; + uuid: string; + recoveryCodes: string[]; + createdDate: number; + lastAccessDate: number; + sharedSecret: string; + deviceName: string; + lastLogin: number; + counter: number; + checksumDigit: boolean; + truncationOffset: number; + clockDriftSeconds: number; +}; + +// @public (undocumented) +export interface DeletedPushDevice { + // (undocumented) + communicationId: string; + // (undocumented) + communicationType: string; + // (undocumented) + createdDate: number; + // (undocumented) + deviceId: string; + // (undocumented) + deviceMechanismUID: string; + // (undocumented) + deviceName: string; + // (undocumented) + deviceType: string; + // (undocumented) + _id: string; + // (undocumented) + issuer: string; + // (undocumented) + lastAccessDate: number; + // (undocumented) + recoveryCodes: string[]; + // (undocumented) + _rev: string; + // (undocumented) + sharedSecret: string; + // (undocumented) + uuid: string; +} + +// @public (undocumented) +export type DeleteOathQuery = { + realm?: string; + userId: string; + uuid: string; +}; + +// @public (undocumented) +export type Device = { + _id: string; + _rev: string; + createdDate: number; + lastAccessDate: number; + deviceId: string; + deviceName: string; + uuid: string; + recoveryCodes: string[]; + key: { + kty: string; + kid: string; + use: string; + alg: string; + n: string; + e: string; + }; + deviceManagementStatus: boolean; +}; + +// @public (undocumented) +export type DeviceClient = ReturnType; + +// @public +export const deviceClient: (config: LegacyConfigOptions) => { + oath: { + get: (query: RetrieveOathQuery) => Promise; + delete: (query: RetrieveOathQuery & { + device: OathDevice; + }) => Promise; + }; + push: { + get: (query: PushDeviceQuery) => Promise; + delete: (query: DeleteDeviceQuery) => Promise; + }; + webAuthn: { + get: (query: WebAuthnQuery) => Promise; + update: (query: WebAuthnQuery & { + device: WebAuthnDevice; + }) => Promise; + delete: (query: WebAuthnQuery & { + device: WebAuthnDevice | UpdatedWebAuthnDevice; + }) => Promise; + }; + bound: { + get: (query: GetBoundDevicesQuery) => Promise; + delete: (query: BoundDeviceQuery) => Promise; + update: (query: BoundDeviceQuery) => Promise; + }; + profile: { + get: (query: GetProfileDevices) => Promise; + update: (query: ProfileDevicesQuery) => Promise; + delete: (query: ProfileDevicesQuery) => Promise; + }; +}; + +// @public (undocumented) +export type DeviceInfo = { + _id: string; + _rev: string; + identifier: string; + metadata: { + platform: { + platform: string; + version: number; + device: string; + deviceName: string; + model: string; + brand: string; + locale: string; + timeZone: string; + jailBreakScore: number; + }; + hardware: { + hardware: string; + manufacturer: string; + storage: number; + memory: number; + cpu: number; + display: { + width: number; + height: number; + orientation: number; + }; + camera: { + numberOfCameras: number; + }; + }; + browser: { + userAgent: string; + }; + bluetooth: { + supported: boolean; + }; + network: { + connected: boolean; + }; + telephony: { + networkCountryIso: string; + carrierName: string; + }; + }; + lastSelectedDate: number; + alias: string; + recoveryCodes: string[]; +}; + +// @public (undocumented) +export type DeviceInfoResponse = { + result: DeviceInfo[]; + resultCount: number; + pagedResultsCookie: null; + totalPagedResultsPolicy: string; + totalPagedResults: -1; + remainingPagedResults: -1; +}; + +// @public (undocumented) +export type DeviceMetadata = { + platform: string; + version: 34; + device: string; + deviceName: string; + model: string; + brand: string; + locale: string; + timeZone: string; + jailBreakScore: 0; +}; + +// @public (undocumented) +export type DeviceProfile = { + _id: string; + _rev: string; + identifier: string; + metadata: Metadata; + lastSelectedDate: number; + alias: 'Test3'; + recoveryCodes: []; +}; + +// @public (undocumented) +export type DeviceResponse = { + result: Device[]; + resultCount: number; + pagedResultsCookie: null; + totalPagedResultsPolicy: string; + totalPagedResults: -1; + remainingPagedResults: -1; +}; + +// @public (undocumented) +export type GetBoundDevicesQuery = { + userId: string; + realm?: string; +}; + +// @public (undocumented) +export interface GetProfileDevices { + // (undocumented) + realm?: string; + // (undocumented) + userId: string; +} + +// @public (undocumented) +export type Hardware = { + hardware: string; + manufacturer: string; + storage: 5939; + memory: 2981; + cpu: 4; + display: { + width: 1440; + height: 2678; + orientation: 1; + }; + camera: { + numberOfCameras: 2; + }; +}; + +// @public (undocumented) +export type Metadata = { + platform: DeviceMetadata; + hardware: Hardware; + browser: Browser; + bluetooth: Bluetooth; + network: Network; + telephony: Telephony; +}; + +// @public (undocumented) +export type Network = { + connected: true; +}; + +// @public (undocumented) +export type OathDevice = { + _id: string; + deviceManagementStatus: boolean; + deviceName: string; + uuid: string; + createdDate: number; + lastAccessDate: number; + _rev: string; +}; + +// @public (undocumented) +export type OathResponse = { + pagedResultsCookie: string | null; + remainingPagedResults: number; + resultCount: number; + totalPagedResults: number; + totalPagedResultsPolicy: string; + result: OathDevice[]; +}; + +// @public (undocumented) +export type ProfileDevice = { + _id: string; + _rev: string; + identifier: string; + metadata: { + platform: { + platform: string; + version: number; + device: string; + deviceName: string; + model: string; + brand: string; + locale: string; + timeZone: string; + jailBreakScore: number; + }; + hardware: { + hardware: string; + manufacturer: string; + storage: number; + memory: number; + cpu: number; + display: { + width: number; + height: number; + orientation: number; + }; + camera: { + numberOfCameras: number; + }; + }; + browser: { + userAgent: string; + }; + bluetooth: { + supported: boolean; + }; + network: { + connected: boolean; + }; + telephony: { + networkCountryIso: string; + carrierName: string; + }; + }; + lastSelectedDate: number; + alias: string; + recoveryCodes: string[]; +}; + +// @public (undocumented) +export interface ProfileDevicesQuery extends GetProfileDevices { + // (undocumented) + device: ProfileDevice; +} + +// @public (undocumented) +export type PushDevice = { + _id: string; + _rev: string; + createdDate: number; + lastAccessDate: number; + deviceName: string; + uuid: string; + deviceManagementStatus: boolean; +}; + +// @public (undocumented) +export type PushDeviceBody = { + id: string; + deviceName: string; + uuid: string; + createdDate: string; + lastAccessDate: string; +}; + +// @public (undocumented) +export type PushDeviceQuery = { + realm?: string; + userId: string; +}; + +// @public (undocumented) +export type RetrieveOathQuery = { + realm?: string; + userId: string; +}; + +// @public (undocumented) +export type Telephony = { + networkCountryIso: 'us'; + carrierName: 'T-Mobile'; +}; + +// @public (undocumented) +export type UpdatedWebAuthnDevice = { + _id: string; + _rev: string; + uuid: string; + recoveryCodes: string[]; + createdDate: number; + lastAccessDate: number; + credentialId: string; + algorithm: string; + deviceName: string; + signCount: number; + key: { + kty: string; + x: string; + y: string; + crv: string; + }; +}; + +// @public (undocumented) +export type WebAuthnBody = { + id: string; + deviceName: string; + uuid: string; + credentialId: string; + createdDate: number; + lastAccessDate: number; +}; + +// @public (undocumented) +export type WebAuthnCredential = { + _id: string; + _rev: string; + uuid: string; + recoveryCodes: string[]; + createdDate: number; + lastAccessDate: number; + credentialId: string; + algorithm: string; + deviceName: string; + key: { + kty: string; + x: string; + y: string; + crv: string; + }; +}; + +// @public (undocumented) +export type WebAuthnDevice = { + _id: string; + _rev: string; + createdDate: number; + lastAccessDate: number; + credentialId: string; + deviceName: string; + uuid: string; + deviceManagementStatus: boolean; +}; + +// @public (undocumented) +export type WebAuthnQuery = { + realm?: string; + userId: string; +}; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/journey-client/README.md b/packages/journey-client/README.md index 910ca9462ee..5d2d0e68309 100644 --- a/packages/journey-client/README.md +++ b/packages/journey-client/README.md @@ -13,6 +13,7 @@ - [API Reference](#api-reference) - [Working with Callbacks](#working-with-callbacks) - [Request Middleware](#request-middleware) +- [Sharing a Store With Another Client](#sharing-a-store-with-another-client) - [Error Handling](#error-handling) - [Building](#building) - [Testing](#testing) @@ -109,28 +110,33 @@ The client automatically derives all needed configuration from the well-known re ### `journey(options)` -Factory function that creates a journey client instance. Throws on initialization failure (invalid URL, fetch error, non-AM server). +Factory function that creates a journey client instance. It returns an argument error for invalid input or an already-occupied shared store, and throws for initialization failures after input validation (invalid URL, fetch error, non-AM server). ```typescript const client = await journey({ config: JourneyClientConfig, requestMiddleware?: RequestMiddleware[], logger?: { level: LogLevel; custom?: CustomLogger }, + store?: SdkStore, }); ``` -**Returns**: `Promise` +**Returns**: `Promise` -**Throws**: `Error` if the wellknown URL is invalid, the fetch fails, or the server is not a ForgeRock AM instance. +Invalid input, including an invalid `store` handle or attaching a second journey client to a shared store, is returned as an `argument_error`. ```typescript -try { - const client = await journey({ config }); -} catch (error) { - console.error('Initialization failed:', error.message); +const result = await journey({ config }); + +if ('error' in result) { + console.error('Invalid configuration:', result.error); +} else { + const step = await result.start({ journey: 'Login' }); } ``` +**Throws**: `Error` if the wellknown URL is invalid, the fetch fails, or the server is not a ForgeRock AM instance. + ### Client Methods #### `client.start(options?)` @@ -232,6 +238,50 @@ const client = await journey({ | `JOURNEY_NEXT` | Submitting a step | | `JOURNEY_TERMINATE` | Terminating the session | +## Sharing a Store With Another Client + +If your application also uses `@forgerock/oidc-client`, the two can share one Redux store so the well-known discovery document is fetched once rather than once per client. + +`journey()` exposes the store it created as `client.store`. Pass it to the other client: + +```typescript +import { journey } from '@forgerock/journey-client'; +import { oidc } from '@forgerock/oidc-client'; + +const journeyClient = await journey({ config }); + +// Attaches to journey's store; the discovery document is already cached there. +const oidcClient = await oidc({ config: oidcConfig, store: journeyClient.store }); +``` + +Or create the store yourself when neither client is the natural owner: + +```typescript +import { createSdkStore } from '@forgerock/sdk-store'; + +const store = createSdkStore(); +const journeyClient = await journey({ config, store }); +const oidcClient = await oidc({ config: oidcConfig, store }); +``` + +Omitting `store` is always valid — the client creates its own, which is the default behaviour. + +### Middleware and logging stay private + +Sharing a store shares cached data, not configuration. `requestMiddleware` and `logger` are registered against the client you pass them to, and are resolved only by that client's own requests: + +```typescript +const store = createSdkStore(); + +// Runs for JOURNEY_START, JOURNEY_NEXT and JOURNEY_TERMINATE only. +await journey({ config, store, requestMiddleware: [journeyMiddleware] }); + +// Runs for OIDC requests only. +await oidc({ config: oidcConfig, store, requestMiddleware: [oidcMiddleware] }); +``` + +Middleware passed here will never run against an OIDC token exchange, and vice versa. + ## Error Handling The `journey()` factory throws on initialization failure. Use try/catch: diff --git a/packages/journey-client/api-report/journey-client.api.md b/packages/journey-client/api-report/journey-client.api.md index 67d98aac9c1..a41f476b290 100644 --- a/packages/journey-client/api-report/journey-client.api.md +++ b/packages/journey-client/api-report/journey-client.api.md @@ -1,507 +1,543 @@ -## API Report File for "@forgerock/journey-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { AuthResponse } from '@forgerock/sdk-types'; -import { Callback } from '@forgerock/sdk-types'; -import { CallbackType } from '@forgerock/sdk-types'; -import { callbackType } from '@forgerock/sdk-types'; -import { createWellknownError } from '@forgerock/sdk-utilities'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import { FailedPolicyRequirement } from '@forgerock/sdk-types'; -import { FailureDetail } from '@forgerock/sdk-types'; -import { GenericError } from '@forgerock/sdk-types'; -import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; -import { JourneyClientConfig } from '@forgerock/sdk-types'; -import { JourneyServerConfig } from '@forgerock/sdk-types'; -import { LegacyServerConfig } from '@forgerock/sdk-types'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { makeJourneyConfig } from '@forgerock/sdk-utilities'; -import { NameValue } from '@forgerock/sdk-types'; -import { PolicyKey } from '@forgerock/sdk-types'; -import { PolicyParams } from '@forgerock/sdk-types'; -import { PolicyRequirement } from '@forgerock/sdk-types'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { Step } from '@forgerock/sdk-types'; -import { StepDetail } from '@forgerock/sdk-types'; -import { StepType } from '@forgerock/sdk-types'; -import { WellknownResponse } from '@forgerock/sdk-types'; - -export { ActionTypes }; - -// @public -export class AttributeInputCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getName(): string; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setValidateOnly(value: boolean): void; - setValue(value: T): void; -} - -export { AuthResponse }; - -// @public -export class BaseCallback { - constructor(payload: Callback); - getInputValue(selector?: number | string): unknown; - getOutputByName(name: string, defaultValue: T): T; - getOutputValue(selector?: number | string): unknown; - getType(): CallbackType; - // (undocumented) - payload: Callback; - setInputValue(value: unknown, selector?: number | string | RegExp): void; -} - -export { Callback }; - -// @public (undocumented) -export type CallbackFactory = (callback: Callback) => BaseCallback; - -export { CallbackType }; - -export { callbackType }; - -// @public -export class ChoiceCallback extends BaseCallback { - constructor(payload: Callback); - getChoices(): string[]; - getDefaultChoice(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setChoiceIndex(index: number): void; - setChoiceValue(value: string): void; -} - -// @public -export class ConfirmationCallback extends BaseCallback { - constructor(payload: Callback); - getDefaultOption(): number; - getMessageType(): number; - getOptions(): string[]; - getOptionType(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setOptionIndex(index: number): void; - setOptionValue(value: string): void; -} - -// @public (undocumented) -export function createCallback(callback: Callback): BaseCallback; - -export { createWellknownError }; - -export { CustomLogger }; - -// @public -export class DeviceProfileCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - isLocationRequired(): boolean; - isMetadataRequired(): boolean; - // (undocumented) - payload: Callback; - setProfile(profile: DeviceProfileData): void; -} - -// @public (undocumented) -export interface DeviceProfileData { - // (undocumented) - identifier: string; - // (undocumented) - location?: Geolocation_2 | Record; - // (undocumented) - metadata?: { - hardware: { - display: { - [key: string]: string | number | null; - }; - [key: string]: any; - }; - browser: { - [key: string]: string | number | null; - }; - platform: { - [key: string]: string | number | null; - }; - }; -} - -export { FailedPolicyRequirement }; - -export { FailureDetail }; - -export { GenericError }; - -// @public (undocumented) -interface Geolocation_2 { - // (undocumented) - latitude: number; - // (undocumented) - longitude: number; -} -export { Geolocation_2 as Geolocation }; - -// @public -export class HiddenValueCallback extends BaseCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface IdPValue { - // (undocumented) - provider: string; - // (undocumented) - uiConfig: { - [key: string]: string; - }; -} - -// @internal -export interface InternalJourneyClientConfig { - // (undocumented) - error?: GenericError; - // (undocumented) - serverConfig: ResolvedServerConfig; -} - -export { isValidWellknownUrl }; - -// @public -export function journey(input: { - config: JourneyClientConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; -}): Promise; - -// @public -export interface JourneyClient { - // (undocumented) - next: (step: JourneyStep, options?: NextOptions) => Promise; - // (undocumented) - redirect: (step: JourneyStep) => Promise; - // (undocumented) - resume: (url: string, options?: ResumeOptions) => Promise; - // (undocumented) - start: (options?: StartParam) => Promise; - // (undocumented) - subscribe: (listener: () => void) => () => void; - // (undocumented) - terminate: (options?: { query?: Record }) => Promise; -} - -export { JourneyClientConfig }; - -// @public (undocumented) -export type JourneyLoginFailure = AuthResponse & { - type: StepType.LoginFailure; - payload: Step; - getCode: () => number; - getDetail: () => FailureDetail | undefined; - getMessage: () => string | undefined; - getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; - getReason: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyLoginSuccess = AuthResponse & { - type: StepType.LoginSuccess; - payload: Step; - getRealm: () => string | undefined; - getSessionToken: () => string | undefined; - getSuccessUrl: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; - -export { JourneyServerConfig }; - -// @public (undocumented) -export type JourneyStep = AuthResponse & { - type: StepType.Step; - payload: Step; - callbacks: BaseCallback[]; - getCallbackOfType: (type: CallbackType) => T; - getCallbacksOfType: (type: CallbackType) => T[]; - setCallbackValue: (type: CallbackType, value: unknown) => void; - getDescription: () => string | undefined; - getHeader: () => string | undefined; - getStage: () => string | undefined; -}; - -// @public -export class KbaCreateCallback extends BaseCallback { - constructor(payload: Callback); - getPredefinedQuestions(): string[]; - getPrompt(): string; - isAllowedUserDefinedQuestions(): boolean; - // (undocumented) - payload: Callback; - setAnswer(answer: string): void; - setQuestion(question: string): void; -} - -export { LegacyServerConfig }; - -export { LogLevel }; - -export { makeJourneyConfig }; - -// @public (undocumented) -export interface MessageCreator { - // (undocumented) - [key: string]: ( - propertyName: string, - params?: { - [key: string]: unknown; - }, - ) => string; -} - -// @public -export class MetadataCallback extends BaseCallback { - constructor(payload: Callback); - getData(): T; - // (undocumented) - payload: Callback; -} - -// @public -export class NameCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setName(name: string): void; -} - -export { NameValue }; - -// @public (undocumented) -export interface NextOptions { - // (undocumented) - query?: Record; -} - -// @public -export class PasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): string[]; - getPrompt(): string; - // (undocumented) - payload: Callback; - setPassword(password: string): void; -} - -// @public -export class PingOneProtectEvaluationCallback extends BaseCallback { - constructor(payload: Callback); - getPauseBehavioralData(): boolean; - // (undocumented) - payload: Callback; - setClientError(errorMessage: string): void; - setData(data: string): void; -} - -// @public -export class PingOneProtectInitializeCallback extends BaseCallback { - constructor(payload: Callback); - getConfig(): - | Record - | { - envId: string; - consoleLogEnabled: boolean; - deviceAttributesToIgnore: string[]; - customHost: string; - lazyMetadata: boolean; - behavioralDataCollection: boolean; - deviceKeyRsyncIntervals: number; - enableTrust: boolean; - disableTags: boolean; - disableHub: boolean; - }; - // (undocumented) - payload: Callback; - // (undocumented) - setClientError(errorMessage: string): void; -} - -export { PolicyKey }; - -export { PolicyParams }; - -export { PolicyRequirement }; - -// @public -export class PollingWaitCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getWaitTime(): number; - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface ProcessedPropertyError { - // (undocumented) - detail: FailedPolicyRequirement; - // (undocumented) - messages: string[]; -} - -// @public -export class ReCaptchaCallback extends BaseCallback { - constructor(payload: Callback); - getSiteKey(): string; - // (undocumented) - payload: Callback; - setResult(result: string): void; -} - -// @public -export class ReCaptchaEnterpriseCallback extends BaseCallback { - constructor(payload: Callback); - getApiUrl(): string; - getElementClass(): string; - getSiteKey(): string; - // (undocumented) - payload: Callback; - setAction(action: string): void; - setClientError(error: string): void; - setPayload(payload: unknown): void; - setResult(result: string): void; -} - -// @public -export class RedirectCallback extends BaseCallback { - constructor(payload: Callback); - getRedirectUrl(): string; - // (undocumented) - payload: Callback; -} - -export { RequestMiddleware }; - -// @public -export interface ResolvedServerConfig { - // (undocumented) - baseUrl: string; - // (undocumented) - paths: { - authenticate: string; - sessions: string; - }; -} - -// @public (undocumented) -export interface ResumeOptions { - // (undocumented) - journey?: string; - // (undocumented) - query?: Record; -} - -// @public -export class SelectIdPCallback extends BaseCallback { - constructor(payload: Callback); - getProviders(): IdPValue[]; - // (undocumented) - payload: Callback; - setProvider(value: string): void; -} - -// @public (undocumented) -export interface StartParam { - // (undocumented) - journey: string; - // (undocumented) - query?: Record; -} - -export { Step }; - -export { StepDetail }; - -export { StepType }; - -// @public -export class SuspendedTextOutputCallback extends TextOutputCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public -export class TermsAndConditionsCallback extends BaseCallback { - constructor(payload: Callback); - getCreateDate(): Date | null; - getTerms(): string; - getVersion(): string; - // (undocumented) - payload: Callback; - setAccepted(accepted?: boolean): void; -} - -// @public -export class TextInputCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setInput(input: string): void; -} - -// @public -export class TextOutputCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getMessageType(): string; - // (undocumented) - payload: Callback; -} - -// @public -export class ValidatedCreatePasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setPassword(password: string): void; - setValidateOnly(value: boolean): void; -} - -// @public -export class ValidatedCreateUsernameCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setName(name: string): void; - setValidateOnly(value: boolean): void; -} - -export { WellknownResponse }; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/journey-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { AuthResponse } from '@forgerock/sdk-types'; +import { Callback } from '@forgerock/sdk-types'; +import { CallbackType } from '@forgerock/sdk-types'; +import { callbackType } from '@forgerock/sdk-types'; +import { createWellknownError } from '@forgerock/sdk-utilities'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import { FailedPolicyRequirement } from '@forgerock/sdk-types'; +import { FailureDetail } from '@forgerock/sdk-types'; +import { GenericError } from '@forgerock/sdk-types'; +import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; +import { JourneyClientConfig } from '@forgerock/sdk-types'; +import { JourneyServerConfig } from '@forgerock/sdk-types'; +import { LegacyServerConfig } from '@forgerock/sdk-types'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { makeJourneyConfig } from '@forgerock/sdk-utilities'; +import { NameValue } from '@forgerock/sdk-types'; +import { PolicyKey } from '@forgerock/sdk-types'; +import { PolicyParams } from '@forgerock/sdk-types'; +import { PolicyRequirement } from '@forgerock/sdk-types'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; +import { Step } from '@forgerock/sdk-types'; +import { StepDetail } from '@forgerock/sdk-types'; +import { StepType } from '@forgerock/sdk-types'; +import { WellknownResponse } from '@forgerock/sdk-types'; + +export { ActionTypes } + +// @public +export class AttributeInputCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getName(): string; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setValidateOnly(value: boolean): void; + setValue(value: T): void; +} + +export { AuthResponse } + +// @public +export class BaseCallback { + constructor(payload: Callback); + getInputValue(selector?: number | string): unknown; + getOutputByName(name: string, defaultValue: T): T; + getOutputValue(selector?: number | string): unknown; + getType(): CallbackType; + // (undocumented) + payload: Callback; + setInputValue(value: unknown, selector?: number | string | RegExp): void; +} + +export { Callback } + +// @public (undocumented) +export type CallbackFactory = (callback: Callback) => BaseCallback; + +export { CallbackType } + +export { callbackType } + +// @public +export class ChoiceCallback extends BaseCallback { + constructor(payload: Callback); + getChoices(): string[]; + getDefaultChoice(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setChoiceIndex(index: number): void; + setChoiceValue(value: string): void; +} + +// @public +export class ConfirmationCallback extends BaseCallback { + constructor(payload: Callback); + getDefaultOption(): number; + getMessageType(): number; + getOptions(): string[]; + getOptionType(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setOptionIndex(index: number): void; + setOptionValue(value: string): void; +} + +// @public (undocumented) +export function createCallback(callback: Callback): BaseCallback; + +export { createWellknownError } + +export { CustomLogger } + +// @public +export class DeviceProfileCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + isLocationRequired(): boolean; + isMetadataRequired(): boolean; + // (undocumented) + payload: Callback; + setProfile(profile: DeviceProfileData): void; +} + +// @public (undocumented) +export interface DeviceProfileData { + // (undocumented) + identifier: string; + // (undocumented) + location?: Geolocation_2 | Record; + // (undocumented) + metadata?: { + hardware: { + display: { + [key: string]: string | number | null; + }; + [key: string]: any; + }; + browser: { + [key: string]: string | number | null; + }; + platform: { + [key: string]: string | number | null; + }; + }; +} + +export { FailedPolicyRequirement } + +export { FailureDetail } + +export { GenericError } + +// @public (undocumented) +interface Geolocation_2 { + // (undocumented) + latitude: number; + // (undocumented) + longitude: number; +} +export { Geolocation_2 as Geolocation } + +// @public +export class HiddenValueCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface IdPValue { + // (undocumented) + provider: string; + // (undocumented) + uiConfig: { + [key: string]: string; + }; +} + +// @internal +export interface InternalJourneyClientConfig { + // (undocumented) + error?: GenericError; + // (undocumented) + serverConfig: ResolvedServerConfig; +} + +export { isValidWellknownUrl } + +// @public +export function journey(input: { + config: JourneyClientConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; + store?: unknown; +}): Promise; + +// @public +export interface JourneyClient { + // (undocumented) + next: (step: JourneyStep, options?: NextOptions) => Promise; + // (undocumented) + redirect: (step: JourneyStep) => Promise; + // (undocumented) + resume: (url: string, options?: ResumeOptions) => Promise; + // (undocumented) + start: (options?: StartParam) => Promise; + // (undocumented) + store: SdkStore; + // (undocumented) + subscribe: (listener: () => void) => () => void; + // (undocumented) + terminate: (options?: { + query?: Record; + }) => Promise; +} + +export { JourneyClientConfig } + +// @public (undocumented) +export type JourneyLoginFailure = AuthResponse & { + type: StepType.LoginFailure; + payload: Step; + getCode: () => number; + getDetail: () => FailureDetail | undefined; + getMessage: () => string | undefined; + getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; + getReason: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyLoginSuccess = AuthResponse & { + type: StepType.LoginSuccess; + payload: Step; + getRealm: () => string | undefined; + getSessionToken: () => string | undefined; + getSuccessUrl: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; + +export { JourneyServerConfig } + +// @public (undocumented) +export type JourneyStep = AuthResponse & { + type: StepType.Step; + payload: Step; + callbacks: BaseCallback[]; + getCallbackOfType: (type: CallbackType) => T; + getCallbacksOfType: (type: CallbackType) => T[]; + setCallbackValue: (type: CallbackType, value: unknown) => void; + getDescription: () => string | undefined; + getHeader: () => string | undefined; + getStage: () => string | undefined; +}; + +// @public +export class KbaCreateCallback extends BaseCallback { + constructor(payload: Callback); + getPredefinedQuestions(): string[]; + getPrompt(): string; + isAllowedUserDefinedQuestions(): boolean; + // (undocumented) + payload: Callback; + setAnswer(answer: string): void; + setQuestion(question: string): void; +} + +export { LegacyServerConfig } + +export { LogLevel } + +export { makeJourneyConfig } + +// @public (undocumented) +export interface MessageCreator { + // (undocumented) + [key: string]: (propertyName: string, params?: { + [key: string]: unknown; + }) => string; +} + +// @public +export class MetadataCallback extends BaseCallback { + constructor(payload: Callback); + getData(): T; + // (undocumented) + payload: Callback; +} + +// @public +export class NameCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setName(name: string): void; +} + +export { NameValue } + +// @public (undocumented) +export interface NextOptions { + // (undocumented) + query?: Record; +} + +// @public +export class PasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): string[]; + getPrompt(): string; + // (undocumented) + payload: Callback; + setPassword(password: string): void; +} + +// @public +export class PingOneProtectEvaluationCallback extends BaseCallback { + constructor(payload: Callback); + getPauseBehavioralData(): boolean; + // (undocumented) + payload: Callback; + setClientError(errorMessage: string): void; + setData(data: string): void; +} + +// @public +export class PingOneProtectInitializeCallback extends BaseCallback { + constructor(payload: Callback); + getConfig(): Record | { + envId: string; + consoleLogEnabled: boolean; + deviceAttributesToIgnore: string[]; + customHost: string; + lazyMetadata: boolean; + behavioralDataCollection: boolean; + deviceKeyRsyncIntervals: number; + enableTrust: boolean; + disableTags: boolean; + disableHub: boolean; + }; + // (undocumented) + payload: Callback; + // (undocumented) + setClientError(errorMessage: string): void; +} + +// @public +export class PingOneRecognizeCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + getCustomerName(): string; + // (undocumented) + getOperationType(): PingOneRecognizeOperationType; + // (undocumented) + getOptions(): Record; + // (undocumented) + getServiceURL(): string; + // (undocumented) + getTransactionData(): string; + // (undocumented) + getUsername(): string; + // (undocumented) + payload: Callback; + // (undocumented) + setClientError(errorMessage: string): void; + // (undocumented) + setClientErrorCode(errorCode: string): void; + // (undocumented) + setRecognizeId(recognizeId: string): void; + // (undocumented) + setSignedJwt(jwt: string): void; +} + +// @public (undocumented) +export type PingOneRecognizeOperationType = 'ENROLL' | 'AUTHENTICATE'; + +export { PolicyKey } + +export { PolicyParams } + +export { PolicyRequirement } + +// @public +export class PollingWaitCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getWaitTime(): number; + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface ProcessedPropertyError { + // (undocumented) + detail: FailedPolicyRequirement; + // (undocumented) + messages: string[]; +} + +// @public +export class ReCaptchaCallback extends BaseCallback { + constructor(payload: Callback); + getSiteKey(): string; + // (undocumented) + payload: Callback; + setResult(result: string): void; +} + +// @public +export class ReCaptchaEnterpriseCallback extends BaseCallback { + constructor(payload: Callback); + getApiUrl(): string; + getElementClass(): string; + getSiteKey(): string; + // (undocumented) + payload: Callback; + setAction(action: string): void; + setClientError(error: string): void; + setPayload(payload: unknown): void; + setResult(result: string): void; +} + +// @public +export class RedirectCallback extends BaseCallback { + constructor(payload: Callback); + getRedirectUrl(): string; + // (undocumented) + payload: Callback; +} + +export { RequestMiddleware } + +// @public +export interface ResolvedServerConfig { + // (undocumented) + baseUrl: string; + // (undocumented) + paths: { + authenticate: string; + sessions: string; + }; +} + +// @public (undocumented) +export interface ResumeOptions { + // (undocumented) + journey?: string; + // (undocumented) + query?: Record; +} + +// @public +export class SelectIdPCallback extends BaseCallback { + constructor(payload: Callback); + getProviders(): IdPValue[]; + // (undocumented) + payload: Callback; + setProvider(value: string): void; +} + +// @public (undocumented) +export interface StartParam { + // (undocumented) + journey: string; + // (undocumented) + query?: Record; +} + +export { Step } + +export { StepDetail } + +export { StepType } + +// @public +export class SuspendedTextOutputCallback extends TextOutputCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public +export class TermsAndConditionsCallback extends BaseCallback { + constructor(payload: Callback); + getCreateDate(): Date | null; + getTerms(): string; + getVersion(): string; + // (undocumented) + payload: Callback; + setAccepted(accepted?: boolean): void; +} + +// @public +export class TextInputCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setInput(input: string): void; +} + +// @public +export class TextOutputCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getMessageType(): string; + // (undocumented) + payload: Callback; +} + +// @public +export class ValidatedCreatePasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setPassword(password: string): void; + setValidateOnly(value: boolean): void; +} + +// @public +export class ValidatedCreateUsernameCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setName(name: string): void; + setValidateOnly(value: boolean): void; +} + +export { WellknownResponse } + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/journey-client/api-report/journey-client.types.api.md b/packages/journey-client/api-report/journey-client.types.api.md index f6b1565f2cb..b24dc831d06 100644 --- a/packages/journey-client/api-report/journey-client.types.api.md +++ b/packages/journey-client/api-report/journey-client.types.api.md @@ -1,491 +1,523 @@ -## API Report File for "@forgerock/journey-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { AuthResponse } from '@forgerock/sdk-types'; -import { Callback } from '@forgerock/sdk-types'; -import { CallbackType } from '@forgerock/sdk-types'; -import { createWellknownError } from '@forgerock/sdk-utilities'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import { FailedPolicyRequirement } from '@forgerock/sdk-types'; -import { FailureDetail } from '@forgerock/sdk-types'; -import { GenericError } from '@forgerock/sdk-types'; -import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; -import { JourneyClientConfig } from '@forgerock/sdk-types'; -import { JourneyServerConfig } from '@forgerock/sdk-types'; -import { LegacyServerConfig } from '@forgerock/sdk-types'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { NameValue } from '@forgerock/sdk-types'; -import { PolicyKey } from '@forgerock/sdk-types'; -import { PolicyParams } from '@forgerock/sdk-types'; -import { PolicyRequirement } from '@forgerock/sdk-types'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { Step } from '@forgerock/sdk-types'; -import { StepDetail } from '@forgerock/sdk-types'; -import { StepType } from '@forgerock/sdk-types'; -import { WellknownResponse } from '@forgerock/sdk-types'; - -export { ActionTypes }; - -// @public -export class AttributeInputCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getName(): string; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setValidateOnly(value: boolean): void; - setValue(value: T): void; -} - -export { AuthResponse }; - -// @public -export class BaseCallback { - constructor(payload: Callback); - getInputValue(selector?: number | string): unknown; - getOutputByName(name: string, defaultValue: T): T; - getOutputValue(selector?: number | string): unknown; - getType(): CallbackType; - // (undocumented) - payload: Callback; - setInputValue(value: unknown, selector?: number | string | RegExp): void; -} - -export { Callback }; - -// @public (undocumented) -export type CallbackFactory = (callback: Callback) => BaseCallback; - -export { CallbackType }; - -// @public -export class ChoiceCallback extends BaseCallback { - constructor(payload: Callback); - getChoices(): string[]; - getDefaultChoice(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setChoiceIndex(index: number): void; - setChoiceValue(value: string): void; -} - -// @public -export class ConfirmationCallback extends BaseCallback { - constructor(payload: Callback); - getDefaultOption(): number; - getMessageType(): number; - getOptions(): string[]; - getOptionType(): number; - getPrompt(): string; - // (undocumented) - payload: Callback; - setOptionIndex(index: number): void; - setOptionValue(value: string): void; -} - -// @public (undocumented) -export function createCallback(callback: Callback): BaseCallback; - -export { createWellknownError }; - -export { CustomLogger }; - -// @public -export class DeviceProfileCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - isLocationRequired(): boolean; - isMetadataRequired(): boolean; - // (undocumented) - payload: Callback; - setProfile(profile: DeviceProfileData): void; -} - -// @public (undocumented) -export interface DeviceProfileData { - // (undocumented) - identifier: string; - // (undocumented) - location?: Geolocation_2 | Record; - // (undocumented) - metadata?: { - hardware: { - display: { - [key: string]: string | number | null; - }; - [key: string]: any; - }; - browser: { - [key: string]: string | number | null; - }; - platform: { - [key: string]: string | number | null; - }; - }; -} - -export { FailedPolicyRequirement }; - -export { FailureDetail }; - -export { GenericError }; - -// @public (undocumented) -interface Geolocation_2 { - // (undocumented) - latitude: number; - // (undocumented) - longitude: number; -} -export { Geolocation_2 as Geolocation }; - -// @public -export class HiddenValueCallback extends BaseCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface IdPValue { - // (undocumented) - provider: string; - // (undocumented) - uiConfig: { - [key: string]: string; - }; -} - -// @internal -export interface InternalJourneyClientConfig { - // (undocumented) - error?: GenericError; - // (undocumented) - serverConfig: ResolvedServerConfig; -} - -export { isValidWellknownUrl }; - -// @public -export interface JourneyClient { - // (undocumented) - next: (step: JourneyStep, options?: NextOptions) => Promise; - // (undocumented) - redirect: (step: JourneyStep) => Promise; - // (undocumented) - resume: (url: string, options?: ResumeOptions) => Promise; - // (undocumented) - start: (options?: StartParam) => Promise; - // (undocumented) - subscribe: (listener: () => void) => () => void; - // (undocumented) - terminate: (options?: { query?: Record }) => Promise; -} - -export { JourneyClientConfig }; - -// @public (undocumented) -export type JourneyLoginFailure = AuthResponse & { - type: StepType.LoginFailure; - payload: Step; - getCode: () => number; - getDetail: () => FailureDetail | undefined; - getMessage: () => string | undefined; - getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; - getReason: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyLoginSuccess = AuthResponse & { - type: StepType.LoginSuccess; - payload: Step; - getRealm: () => string | undefined; - getSessionToken: () => string | undefined; - getSuccessUrl: () => string | undefined; -}; - -// @public (undocumented) -export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; - -export { JourneyServerConfig }; - -// @public (undocumented) -export type JourneyStep = AuthResponse & { - type: StepType.Step; - payload: Step; - callbacks: BaseCallback[]; - getCallbackOfType: (type: CallbackType) => T; - getCallbacksOfType: (type: CallbackType) => T[]; - setCallbackValue: (type: CallbackType, value: unknown) => void; - getDescription: () => string | undefined; - getHeader: () => string | undefined; - getStage: () => string | undefined; -}; - -// @public -export class KbaCreateCallback extends BaseCallback { - constructor(payload: Callback); - getPredefinedQuestions(): string[]; - getPrompt(): string; - isAllowedUserDefinedQuestions(): boolean; - // (undocumented) - payload: Callback; - setAnswer(answer: string): void; - setQuestion(question: string): void; -} - -export { LegacyServerConfig }; - -export { LogLevel }; - -// @public (undocumented) -export interface MessageCreator { - // (undocumented) - [key: string]: ( - propertyName: string, - params?: { - [key: string]: unknown; - }, - ) => string; -} - -// @public -export class MetadataCallback extends BaseCallback { - constructor(payload: Callback); - getData(): T; - // (undocumented) - payload: Callback; -} - -// @public -export class NameCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setName(name: string): void; -} - -export { NameValue }; - -// @public (undocumented) -export interface NextOptions { - // (undocumented) - query?: Record; -} - -// @public -export class PasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): string[]; - getPrompt(): string; - // (undocumented) - payload: Callback; - setPassword(password: string): void; -} - -// @public -export class PingOneProtectEvaluationCallback extends BaseCallback { - constructor(payload: Callback); - getPauseBehavioralData(): boolean; - // (undocumented) - payload: Callback; - setClientError(errorMessage: string): void; - setData(data: string): void; -} - -// @public -export class PingOneProtectInitializeCallback extends BaseCallback { - constructor(payload: Callback); - getConfig(): - | Record - | { - envId: string; - consoleLogEnabled: boolean; - deviceAttributesToIgnore: string[]; - customHost: string; - lazyMetadata: boolean; - behavioralDataCollection: boolean; - deviceKeyRsyncIntervals: number; - enableTrust: boolean; - disableTags: boolean; - disableHub: boolean; - }; - // (undocumented) - payload: Callback; - // (undocumented) - setClientError(errorMessage: string): void; -} - -export { PolicyKey }; - -export { PolicyParams }; - -export { PolicyRequirement }; - -// @public -export class PollingWaitCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getWaitTime(): number; - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export interface ProcessedPropertyError { - // (undocumented) - detail: FailedPolicyRequirement; - // (undocumented) - messages: string[]; -} - -// @public -export class ReCaptchaCallback extends BaseCallback { - constructor(payload: Callback); - getSiteKey(): string; - // (undocumented) - payload: Callback; - setResult(result: string): void; -} - -// @public -export class ReCaptchaEnterpriseCallback extends BaseCallback { - constructor(payload: Callback); - getApiUrl(): string; - getElementClass(): string; - getSiteKey(): string; - // (undocumented) - payload: Callback; - setAction(action: string): void; - setClientError(error: string): void; - setPayload(payload: unknown): void; - setResult(result: string): void; -} - -// @public -export class RedirectCallback extends BaseCallback { - constructor(payload: Callback); - getRedirectUrl(): string; - // (undocumented) - payload: Callback; -} - -export { RequestMiddleware }; - -// @public -export interface ResolvedServerConfig { - // (undocumented) - baseUrl: string; - // (undocumented) - paths: { - authenticate: string; - sessions: string; - }; -} - -// @public (undocumented) -export interface ResumeOptions { - // (undocumented) - journey?: string; - // (undocumented) - query?: Record; -} - -// @public -export class SelectIdPCallback extends BaseCallback { - constructor(payload: Callback); - getProviders(): IdPValue[]; - // (undocumented) - payload: Callback; - setProvider(value: string): void; -} - -// @public (undocumented) -export interface StartParam { - // (undocumented) - journey: string; - // (undocumented) - query?: Record; -} - -export { Step }; - -export { StepDetail }; - -export { StepType }; - -// @public -export class SuspendedTextOutputCallback extends TextOutputCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public -export class TermsAndConditionsCallback extends BaseCallback { - constructor(payload: Callback); - getCreateDate(): Date | null; - getTerms(): string; - getVersion(): string; - // (undocumented) - payload: Callback; - setAccepted(accepted?: boolean): void; -} - -// @public -export class TextInputCallback extends BaseCallback { - constructor(payload: Callback); - getPrompt(): string; - // (undocumented) - payload: Callback; - setInput(input: string): void; -} - -// @public -export class TextOutputCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getMessageType(): string; - // (undocumented) - payload: Callback; -} - -// @public -export class ValidatedCreatePasswordCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setPassword(password: string): void; - setValidateOnly(value: boolean): void; -} - -// @public -export class ValidatedCreateUsernameCallback extends BaseCallback { - constructor(payload: Callback); - getFailedPolicies(): PolicyRequirement[]; - getPolicies(): Record; - getPrompt(): string; - isRequired(): boolean; - // (undocumented) - payload: Callback; - setName(name: string): void; - setValidateOnly(value: boolean): void; -} - -export { WellknownResponse }; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/journey-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { AuthResponse } from '@forgerock/sdk-types'; +import { Callback } from '@forgerock/sdk-types'; +import { CallbackType } from '@forgerock/sdk-types'; +import { createWellknownError } from '@forgerock/sdk-utilities'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import { FailedPolicyRequirement } from '@forgerock/sdk-types'; +import { FailureDetail } from '@forgerock/sdk-types'; +import { GenericError } from '@forgerock/sdk-types'; +import { isValidWellknownUrl } from '@forgerock/sdk-utilities'; +import { JourneyClientConfig } from '@forgerock/sdk-types'; +import { JourneyServerConfig } from '@forgerock/sdk-types'; +import { LegacyServerConfig } from '@forgerock/sdk-types'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { NameValue } from '@forgerock/sdk-types'; +import { PolicyKey } from '@forgerock/sdk-types'; +import { PolicyParams } from '@forgerock/sdk-types'; +import { PolicyRequirement } from '@forgerock/sdk-types'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; +import { Step } from '@forgerock/sdk-types'; +import { StepDetail } from '@forgerock/sdk-types'; +import { StepType } from '@forgerock/sdk-types'; +import { WellknownResponse } from '@forgerock/sdk-types'; + +export { ActionTypes } + +// @public +export class AttributeInputCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getName(): string; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setValidateOnly(value: boolean): void; + setValue(value: T): void; +} + +export { AuthResponse } + +// @public +export class BaseCallback { + constructor(payload: Callback); + getInputValue(selector?: number | string): unknown; + getOutputByName(name: string, defaultValue: T): T; + getOutputValue(selector?: number | string): unknown; + getType(): CallbackType; + // (undocumented) + payload: Callback; + setInputValue(value: unknown, selector?: number | string | RegExp): void; +} + +export { Callback } + +// @public (undocumented) +export type CallbackFactory = (callback: Callback) => BaseCallback; + +export { CallbackType } + +// @public +export class ChoiceCallback extends BaseCallback { + constructor(payload: Callback); + getChoices(): string[]; + getDefaultChoice(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setChoiceIndex(index: number): void; + setChoiceValue(value: string): void; +} + +// @public +export class ConfirmationCallback extends BaseCallback { + constructor(payload: Callback); + getDefaultOption(): number; + getMessageType(): number; + getOptions(): string[]; + getOptionType(): number; + getPrompt(): string; + // (undocumented) + payload: Callback; + setOptionIndex(index: number): void; + setOptionValue(value: string): void; +} + +// @public (undocumented) +export function createCallback(callback: Callback): BaseCallback; + +export { createWellknownError } + +export { CustomLogger } + +// @public +export class DeviceProfileCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + isLocationRequired(): boolean; + isMetadataRequired(): boolean; + // (undocumented) + payload: Callback; + setProfile(profile: DeviceProfileData): void; +} + +// @public (undocumented) +export interface DeviceProfileData { + // (undocumented) + identifier: string; + // (undocumented) + location?: Geolocation_2 | Record; + // (undocumented) + metadata?: { + hardware: { + display: { + [key: string]: string | number | null; + }; + [key: string]: any; + }; + browser: { + [key: string]: string | number | null; + }; + platform: { + [key: string]: string | number | null; + }; + }; +} + +export { FailedPolicyRequirement } + +export { FailureDetail } + +export { GenericError } + +// @public (undocumented) +interface Geolocation_2 { + // (undocumented) + latitude: number; + // (undocumented) + longitude: number; +} +export { Geolocation_2 as Geolocation } + +// @public +export class HiddenValueCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface IdPValue { + // (undocumented) + provider: string; + // (undocumented) + uiConfig: { + [key: string]: string; + }; +} + +// @internal +export interface InternalJourneyClientConfig { + // (undocumented) + error?: GenericError; + // (undocumented) + serverConfig: ResolvedServerConfig; +} + +export { isValidWellknownUrl } + +// @public +export interface JourneyClient { + // (undocumented) + next: (step: JourneyStep, options?: NextOptions) => Promise; + // (undocumented) + redirect: (step: JourneyStep) => Promise; + // (undocumented) + resume: (url: string, options?: ResumeOptions) => Promise; + // (undocumented) + start: (options?: StartParam) => Promise; + // (undocumented) + store: SdkStore; + // (undocumented) + subscribe: (listener: () => void) => () => void; + // (undocumented) + terminate: (options?: { + query?: Record; + }) => Promise; +} + +export { JourneyClientConfig } + +// @public (undocumented) +export type JourneyLoginFailure = AuthResponse & { + type: StepType.LoginFailure; + payload: Step; + getCode: () => number; + getDetail: () => FailureDetail | undefined; + getMessage: () => string | undefined; + getProcessedMessage: (messageCreator?: MessageCreator) => ProcessedPropertyError[]; + getReason: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyLoginSuccess = AuthResponse & { + type: StepType.LoginSuccess; + payload: Step; + getRealm: () => string | undefined; + getSessionToken: () => string | undefined; + getSuccessUrl: () => string | undefined; +}; + +// @public (undocumented) +export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError; + +export { JourneyServerConfig } + +// @public (undocumented) +export type JourneyStep = AuthResponse & { + type: StepType.Step; + payload: Step; + callbacks: BaseCallback[]; + getCallbackOfType: (type: CallbackType) => T; + getCallbacksOfType: (type: CallbackType) => T[]; + setCallbackValue: (type: CallbackType, value: unknown) => void; + getDescription: () => string | undefined; + getHeader: () => string | undefined; + getStage: () => string | undefined; +}; + +// @public +export class KbaCreateCallback extends BaseCallback { + constructor(payload: Callback); + getPredefinedQuestions(): string[]; + getPrompt(): string; + isAllowedUserDefinedQuestions(): boolean; + // (undocumented) + payload: Callback; + setAnswer(answer: string): void; + setQuestion(question: string): void; +} + +export { LegacyServerConfig } + +export { LogLevel } + +// @public (undocumented) +export interface MessageCreator { + // (undocumented) + [key: string]: (propertyName: string, params?: { + [key: string]: unknown; + }) => string; +} + +// @public +export class MetadataCallback extends BaseCallback { + constructor(payload: Callback); + getData(): T; + // (undocumented) + payload: Callback; +} + +// @public +export class NameCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setName(name: string): void; +} + +export { NameValue } + +// @public (undocumented) +export interface NextOptions { + // (undocumented) + query?: Record; +} + +// @public +export class PasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): string[]; + getPrompt(): string; + // (undocumented) + payload: Callback; + setPassword(password: string): void; +} + +// @public +export class PingOneProtectEvaluationCallback extends BaseCallback { + constructor(payload: Callback); + getPauseBehavioralData(): boolean; + // (undocumented) + payload: Callback; + setClientError(errorMessage: string): void; + setData(data: string): void; +} + +// @public +export class PingOneProtectInitializeCallback extends BaseCallback { + constructor(payload: Callback); + getConfig(): Record | { + envId: string; + consoleLogEnabled: boolean; + deviceAttributesToIgnore: string[]; + customHost: string; + lazyMetadata: boolean; + behavioralDataCollection: boolean; + deviceKeyRsyncIntervals: number; + enableTrust: boolean; + disableTags: boolean; + disableHub: boolean; + }; + // (undocumented) + payload: Callback; + // (undocumented) + setClientError(errorMessage: string): void; +} + +// @public +export class PingOneRecognizeCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + getCustomerName(): string; + // (undocumented) + getOperationType(): PingOneRecognizeOperationType; + // (undocumented) + getOptions(): Record; + // (undocumented) + getServiceURL(): string; + // (undocumented) + getTransactionData(): string; + // (undocumented) + getUsername(): string; + // (undocumented) + payload: Callback; + // (undocumented) + setClientError(errorMessage: string): void; + // (undocumented) + setClientErrorCode(errorCode: string): void; + // (undocumented) + setRecognizeId(recognizeId: string): void; + // (undocumented) + setSignedJwt(jwt: string): void; +} + +// @public (undocumented) +export type PingOneRecognizeOperationType = 'ENROLL' | 'AUTHENTICATE'; + +export { PolicyKey } + +export { PolicyParams } + +export { PolicyRequirement } + +// @public +export class PollingWaitCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getWaitTime(): number; + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export interface ProcessedPropertyError { + // (undocumented) + detail: FailedPolicyRequirement; + // (undocumented) + messages: string[]; +} + +// @public +export class ReCaptchaCallback extends BaseCallback { + constructor(payload: Callback); + getSiteKey(): string; + // (undocumented) + payload: Callback; + setResult(result: string): void; +} + +// @public +export class ReCaptchaEnterpriseCallback extends BaseCallback { + constructor(payload: Callback); + getApiUrl(): string; + getElementClass(): string; + getSiteKey(): string; + // (undocumented) + payload: Callback; + setAction(action: string): void; + setClientError(error: string): void; + setPayload(payload: unknown): void; + setResult(result: string): void; +} + +// @public +export class RedirectCallback extends BaseCallback { + constructor(payload: Callback); + getRedirectUrl(): string; + // (undocumented) + payload: Callback; +} + +export { RequestMiddleware } + +// @public +export interface ResolvedServerConfig { + // (undocumented) + baseUrl: string; + // (undocumented) + paths: { + authenticate: string; + sessions: string; + }; +} + +// @public (undocumented) +export interface ResumeOptions { + // (undocumented) + journey?: string; + // (undocumented) + query?: Record; +} + +// @public +export class SelectIdPCallback extends BaseCallback { + constructor(payload: Callback); + getProviders(): IdPValue[]; + // (undocumented) + payload: Callback; + setProvider(value: string): void; +} + +// @public (undocumented) +export interface StartParam { + // (undocumented) + journey: string; + // (undocumented) + query?: Record; +} + +export { Step } + +export { StepDetail } + +export { StepType } + +// @public +export class SuspendedTextOutputCallback extends TextOutputCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public +export class TermsAndConditionsCallback extends BaseCallback { + constructor(payload: Callback); + getCreateDate(): Date | null; + getTerms(): string; + getVersion(): string; + // (undocumented) + payload: Callback; + setAccepted(accepted?: boolean): void; +} + +// @public +export class TextInputCallback extends BaseCallback { + constructor(payload: Callback); + getPrompt(): string; + // (undocumented) + payload: Callback; + setInput(input: string): void; +} + +// @public +export class TextOutputCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getMessageType(): string; + // (undocumented) + payload: Callback; +} + +// @public +export class ValidatedCreatePasswordCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setPassword(password: string): void; + setValidateOnly(value: boolean): void; +} + +// @public +export class ValidatedCreateUsernameCallback extends BaseCallback { + constructor(payload: Callback); + getFailedPolicies(): PolicyRequirement[]; + getPolicies(): Record; + getPrompt(): string; + isRequired(): boolean; + // (undocumented) + payload: Callback; + setName(name: string): void; + setValidateOnly(value: boolean): void; +} + +export { WellknownResponse } + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/journey-client/api-report/journey-client.webauthn.api.md b/packages/journey-client/api-report/journey-client.webauthn.api.md index 32e8ec42a86..5b995dd7ad3 100644 --- a/packages/journey-client/api-report/journey-client.webauthn.api.md +++ b/packages/journey-client/api-report/journey-client.webauthn.api.md @@ -1,210 +1,189 @@ -## API Report File for "@forgerock/journey-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import type { AuthResponse } from '@forgerock/sdk-types'; -import type { Callback } from '@forgerock/sdk-types'; -import type { CallbackType } from '@forgerock/sdk-types'; -import type { Step } from '@forgerock/sdk-types'; -import type { StepType } from '@forgerock/sdk-types'; - -// @public (undocumented) -export enum AttestationType { - // (undocumented) - Direct = 'direct', - // (undocumented) - Indirect = 'indirect', - // (undocumented) - None = 'none', -} - -// @public -export class BaseCallback { - constructor(payload: Callback); - getInputValue(selector?: number | string): unknown; - getOutputByName(name: string, defaultValue: T): T; - getOutputValue(selector?: number | string): unknown; - getType(): CallbackType; - // (undocumented) - payload: Callback; - setInputValue(value: unknown, selector?: number | string | RegExp): void; -} - -// @public -export class HiddenValueCallback extends BaseCallback { - constructor(payload: Callback); - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export type JourneyStep = AuthResponse & { - type: StepType.Step; - payload: Step; - callbacks: BaseCallback[]; - getCallbackOfType: (type: CallbackType) => T; - getCallbacksOfType: (type: CallbackType) => T[]; - setCallbackValue: (type: CallbackType, value: unknown) => void; - getDescription: () => string | undefined; - getHeader: () => string | undefined; - getStage: () => string | undefined; -}; - -// @public -export class MetadataCallback extends BaseCallback { - constructor(payload: Callback); - getData(): T; - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export type OutcomeWithName< - ClientId extends string, - Attestation extends AttestationType, - PubKeyCred extends PublicKeyCredential, - Name = '', -> = Name extends infer P extends string - ? `${ClientId}::${Attestation}::${PubKeyCred['id']}${P extends '' ? '' : `::${P}`}` - : never; - -// @public (undocumented) -export interface RelyingParty { - // (undocumented) - id?: string; - // (undocumented) - name: string; -} - -// @public -export class TextOutputCallback extends BaseCallback { - constructor(payload: Callback); - getMessage(): string; - getMessageType(): string; - // (undocumented) - payload: Callback; -} - -// @public (undocumented) -export enum UserVerificationType { - // (undocumented) - Discouraged = 'discouraged', - // (undocumented) - Preferred = 'preferred', - // (undocumented) - Required = 'required', -} - -// @public -export abstract class WebAuthn { - static authenticate(step: JourneyStep, signal?: AbortSignal): Promise; - static createAuthenticationPublicKey( - metadata: WebAuthnAuthenticationMetadata, - ): PublicKeyCredentialRequestOptions; - static createRegistrationPublicKey( - metadata: WebAuthnRegistrationMetadata, - ): PublicKeyCredentialCreationOptions; - static getAuthenticationCredential( - options: PublicKeyCredentialRequestOptions, - mediation?: CredentialMediationRequirement, - signal?: AbortSignal, - ): Promise; - static getAuthenticationOutcome( - credential: PublicKeyCredential | null, - ): - | OutcomeWithName - | OutcomeWithName; - static getCallbacks(step: JourneyStep): WebAuthnCallbacks; - static getMetadataCallback(step: JourneyStep): MetadataCallback | undefined; - static getOutcomeCallback(step: JourneyStep): HiddenValueCallback | undefined; - static getRegistrationCredential( - options: PublicKeyCredentialCreationOptions, - ): Promise; - static getRegistrationOutcome( - credential: PublicKeyCredential | null, - ): OutcomeWithName; - static getTextOutputCallback(step: JourneyStep): TextOutputCallback | undefined; - static getWebAuthnStepType(step: JourneyStep): WebAuthnStepType; - static isConditionalMediationSupported(step?: JourneyStep): Promise; - static register(step: JourneyStep, deviceName?: T): Promise; -} - -// @public (undocumented) -export interface WebAuthnAuthenticationMetadata { - // (undocumented) - acceptableCredentials?: string; - // (undocumented) - allowCredentials?: string; - // (undocumented) - challenge: string; - // (undocumented) - mediation?: CredentialMediationRequirement; - // (undocumented) - relyingPartyId: string; - // (undocumented) - supportsJsonResponse?: boolean; - // (undocumented) - timeout: number; - // (undocumented) - userVerification: UserVerificationType; -} - -// @public (undocumented) -export interface WebAuthnCallbacks { - // (undocumented) - hiddenCallback?: HiddenValueCallback; - // (undocumented) - metadataCallback?: MetadataCallback; - // (undocumented) - textOutputCallback?: TextOutputCallback; -} - -// @public (undocumented) -export enum WebAuthnOutcome { - // (undocumented) - Error = 'ERROR', - // (undocumented) - Unsupported = 'unsupported', -} - -// @public (undocumented) -export interface WebAuthnRegistrationMetadata { - // (undocumented) - attestationPreference: 'none' | 'indirect' | 'direct'; - // (undocumented) - authenticatorSelection: string; - // (undocumented) - challenge: string; - // (undocumented) - displayName?: string; - // (undocumented) - excludeCredentials: string; - // (undocumented) - pubKeyCredParams: string; - // (undocumented) - relyingPartyId: string; - // (undocumented) - relyingPartyName: string; - // (undocumented) - supportsJsonResponse?: boolean; - // (undocumented) - timeout: number; - // (undocumented) - userId: string; - // (undocumented) - userName: string; -} - -// @public (undocumented) -export enum WebAuthnStepType { - // (undocumented) - Authentication = 1, - // (undocumented) - None = 0, - // (undocumented) - Registration = 2, -} - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/journey-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { AuthResponse } from '@forgerock/sdk-types'; +import type { Callback } from '@forgerock/sdk-types'; +import type { CallbackType } from '@forgerock/sdk-types'; +import type { Step } from '@forgerock/sdk-types'; +import type { StepType } from '@forgerock/sdk-types'; + +// @public (undocumented) +export enum AttestationType { + // (undocumented) + Direct = "direct", + // (undocumented) + Indirect = "indirect", + // (undocumented) + None = "none" +} + +// @public +export class BaseCallback { + constructor(payload: Callback); + getInputValue(selector?: number | string): unknown; + getOutputByName(name: string, defaultValue: T): T; + getOutputValue(selector?: number | string): unknown; + getType(): CallbackType; + // (undocumented) + payload: Callback; + setInputValue(value: unknown, selector?: number | string | RegExp): void; +} + +// @public +export class HiddenValueCallback extends BaseCallback { + constructor(payload: Callback); + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export type JourneyStep = AuthResponse & { + type: StepType.Step; + payload: Step; + callbacks: BaseCallback[]; + getCallbackOfType: (type: CallbackType) => T; + getCallbacksOfType: (type: CallbackType) => T[]; + setCallbackValue: (type: CallbackType, value: unknown) => void; + getDescription: () => string | undefined; + getHeader: () => string | undefined; + getStage: () => string | undefined; +}; + +// @public +export class MetadataCallback extends BaseCallback { + constructor(payload: Callback); + getData(): T; + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export type OutcomeWithName = Name extends infer P extends string ? `${ClientId}::${Attestation}::${PubKeyCred['id']}${P extends '' ? '' : `::${P}`}` : never; + +// @public (undocumented) +export interface RelyingParty { + // (undocumented) + id?: string; + // (undocumented) + name: string; +} + +// @public +export class TextOutputCallback extends BaseCallback { + constructor(payload: Callback); + getMessage(): string; + getMessageType(): string; + // (undocumented) + payload: Callback; +} + +// @public (undocumented) +export enum UserVerificationType { + // (undocumented) + Discouraged = "discouraged", + // (undocumented) + Preferred = "preferred", + // (undocumented) + Required = "required" +} + +// @public +export abstract class WebAuthn { + static authenticate(step: JourneyStep, signal?: AbortSignal): Promise; + static createAuthenticationPublicKey(metadata: WebAuthnAuthenticationMetadata): PublicKeyCredentialRequestOptions; + static createRegistrationPublicKey(metadata: WebAuthnRegistrationMetadata): PublicKeyCredentialCreationOptions; + static getAuthenticationCredential(options: PublicKeyCredentialRequestOptions, mediation?: CredentialMediationRequirement, signal?: AbortSignal): Promise; + static getAuthenticationOutcome(credential: PublicKeyCredential | null): OutcomeWithName | OutcomeWithName; + static getCallbacks(step: JourneyStep): WebAuthnCallbacks; + static getMetadataCallback(step: JourneyStep): MetadataCallback | undefined; + static getOutcomeCallback(step: JourneyStep): HiddenValueCallback | undefined; + static getRegistrationCredential(options: PublicKeyCredentialCreationOptions): Promise; + static getRegistrationOutcome(credential: PublicKeyCredential | null): OutcomeWithName; + static getTextOutputCallback(step: JourneyStep): TextOutputCallback | undefined; + static getWebAuthnStepType(step: JourneyStep): WebAuthnStepType; + static isConditionalMediationSupported(step?: JourneyStep): Promise; + static register(step: JourneyStep, deviceName?: T): Promise; +} + +// @public (undocumented) +export interface WebAuthnAuthenticationMetadata { + // (undocumented) + acceptableCredentials?: string; + // (undocumented) + allowCredentials?: string; + // (undocumented) + challenge: string; + // (undocumented) + mediation?: CredentialMediationRequirement; + // (undocumented) + relyingPartyId: string; + // (undocumented) + supportsJsonResponse?: boolean; + // (undocumented) + timeout: number; + // (undocumented) + userVerification: UserVerificationType; +} + +// @public (undocumented) +export interface WebAuthnCallbacks { + // (undocumented) + hiddenCallback?: HiddenValueCallback; + // (undocumented) + metadataCallback?: MetadataCallback; + // (undocumented) + textOutputCallback?: TextOutputCallback; +} + +// @public (undocumented) +export enum WebAuthnOutcome { + // (undocumented) + Error = "ERROR", + // (undocumented) + Unsupported = "unsupported" +} + +// @public (undocumented) +export interface WebAuthnRegistrationMetadata { + // (undocumented) + attestationPreference: 'none' | 'indirect' | 'direct'; + // (undocumented) + authenticatorSelection: string; + // (undocumented) + challenge: string; + // (undocumented) + displayName?: string; + // (undocumented) + excludeCredentials: string; + // (undocumented) + pubKeyCredParams: string; + // (undocumented) + relyingPartyId: string; + // (undocumented) + relyingPartyName: string; + // (undocumented) + supportsJsonResponse?: boolean; + // (undocumented) + timeout: number; + // (undocumented) + userId: string; + // (undocumented) + userName: string; +} + +// @public (undocumented) +export enum WebAuthnStepType { + // (undocumented) + Authentication = 1, + // (undocumented) + None = 0, + // (undocumented) + Registration = 2 +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/journey-client/package.json b/packages/journey-client/package.json index ff2e9148304..0a692a57499 100644 --- a/packages/journey-client/package.json +++ b/packages/journey-client/package.json @@ -34,8 +34,8 @@ }, "dependencies": { "@forgerock/sdk-logger": "workspace:*", - "@forgerock/sdk-oidc": "workspace:*", "@forgerock/sdk-request-middleware": "workspace:*", + "@forgerock/sdk-store": "workspace:*", "@forgerock/sdk-types": "workspace:*", "@forgerock/sdk-utilities": "workspace:*", "@forgerock/storage": "workspace:*", diff --git a/packages/journey-client/src/lib/client.store.effects.ts b/packages/journey-client/src/lib/client.store.effects.ts new file mode 100644 index 00000000000..c6122a0fda8 --- /dev/null +++ b/packages/journey-client/src/lib/client.store.effects.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { createSdkStore, injectClient } from '@forgerock/sdk-store'; + +import { configSlice } from './config.slice.js'; +import { journeyApi } from './journey.api.js'; + +import type { logger as loggerFn } from '@forgerock/sdk-logger'; +import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore, SdkStoreHandle } from '@forgerock/sdk-store'; + +import type { RootState } from './client.store.utils.js'; + +/** Creates or attaches the store backing a Journey client. */ +export const createJourneyStore = ({ + requestMiddleware, + logger, + store, +}: { + requestMiddleware?: RequestMiddleware[]; + logger?: ReturnType; + store?: SdkStore; +}): SdkStoreHandle => + injectClient(store ?? createSdkStore(), { + api: journeyApi, + slices: [configSlice], + requestMiddleware, + logger, + }); diff --git a/packages/journey-client/src/lib/client.store.test.ts b/packages/journey-client/src/lib/client.store.test.ts index 98cfa6a9efa..1f3f383926c 100644 --- a/packages/journey-client/src/lib/client.store.test.ts +++ b/packages/journey-client/src/lib/client.store.test.ts @@ -14,6 +14,7 @@ import { journey } from './client.store.js'; import { createJourneyStep } from './step.utils.js'; import type { GenericError, Step, WellknownResponse } from '../index.js'; +import type { JourneyClient } from './client.store.js'; import type { JourneyClientConfig } from './config.types.js'; /** @@ -108,7 +109,7 @@ describe('journey-client', () => { test('journey_WellknownConfig_ReturnsClientWithAllMethods', async () => { setupMockFetch(); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; expect(client.start).toBeInstanceOf(Function); expect(client.next).toBeInstanceOf(Function); @@ -142,7 +143,7 @@ describe('journey-client', () => { const mockStepResponse: Step = { authId: 'test-auth-id', callbacks: [] }; setupMockFetch(mockStepResponse); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const step = await client.start(); expect(step).toBeDefined(); @@ -169,7 +170,7 @@ describe('journey-client', () => { }; setupMockFetch(failurePayload, 401); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const result = await client.start(); expect(result).toBeDefined(); @@ -207,7 +208,7 @@ describe('journey-client', () => { }; setupMockFetch(nextStepPayload); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const nextStep = await client.next(initialStep, {}); expect(nextStep).toBeDefined(); @@ -237,7 +238,7 @@ describe('journey-client', () => { }; setupMockFetch(failurePayload, 401); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const result = await client.next(initialStep, {}); expect(result).toBeDefined(); @@ -267,7 +268,7 @@ describe('journey-client', () => { vi.stubGlobal('window', { location: { assign: assignMock } }); setupMockFetch(); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; await client.redirect(step); expect(mockStorageInstance.set).toHaveBeenCalledWith({ step: step.payload }); @@ -285,7 +286,7 @@ describe('journey-client', () => { const nextStepPayload: Step = { authId: 'test-auth-id', callbacks: [] }; setupMockFetch(nextStepPayload); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const resumeUrl = 'https://app.com/callback?code=123&state=abc'; const step = await client.resume(resumeUrl, {}); @@ -314,7 +315,7 @@ describe('journey-client', () => { const nextStepPayload: Step = { authId: 'test-auth-id', callbacks: [] }; setupMockFetch(nextStepPayload); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const resumeUrl = 'https://app.com/callback?code=123&state=abc&error=access_denied&errorCode=E1&errorMessage=oops&form_post_entry=fp&nonce=n1&RelayState=rs&responsekey=rk&scope=openid&suspendedId=s1'; await client.resume(resumeUrl, {}); @@ -342,7 +343,7 @@ describe('journey-client', () => { const nextStepPayload: Step = { authId: 'test-auth-id', callbacks: [] }; setupMockFetch(nextStepPayload); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const resumeUrl = 'https://app.com/callback?code=123&state=abc'; await client.resume(resumeUrl, { query: { code: 'override' } }); @@ -363,7 +364,7 @@ describe('journey-client', () => { const nextStepPayload: Step = { authId: 'test-auth-id', callbacks: [] }; setupMockFetch(nextStepPayload); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const resumeUrl = 'https://app.com/callback?code=123&state=abc'; const step = await client.resume(resumeUrl, {}); @@ -384,7 +385,7 @@ describe('journey-client', () => { mockStorageInstance.get.mockResolvedValue(undefined); setupMockFetch(); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const resumeUrl = 'https://app.com/callback?code=123&state=abc'; await expect(client.resume(resumeUrl)).rejects.toThrow( @@ -399,7 +400,7 @@ describe('journey-client', () => { const mockStepResponse: Step = { authId: 'test-auth-id', callbacks: [] }; setupMockFetch(mockStepResponse); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const resumeUrl = 'https://app.com/callback?foo=bar'; const step = await client.resume(resumeUrl, {}); @@ -419,7 +420,7 @@ describe('journey-client', () => { test('start_NoDataFromServer_ReturnsGenericError', async () => { setupMockFetch(null); - const client = await journey({ config: mockConfig }); + const client = (await journey({ config: mockConfig })) as JourneyClient; const result = await client.start(); expect(isGenericError(result)).toBe(true); @@ -453,7 +454,7 @@ describe('journey-client', () => { return Promise.resolve(new Response(JSON.stringify(mockStepResponse))); }); - const client = await journey({ config: localhostConfig }); + const client = (await journey({ config: localhostConfig })) as JourneyClient; await client.start(); expect(mockFetch).toHaveBeenCalledTimes(2); @@ -553,7 +554,7 @@ describe('journey-client', () => { return Promise.resolve(new Response(JSON.stringify(mockStepResponse))); }); - const client = await journey({ config: alphaConfig }); + const client = (await journey({ config: alphaConfig })) as JourneyClient; await client.start(); const request = mockFetch.mock.calls[1][0] as Request; @@ -581,9 +582,9 @@ describe('journey-client', () => { test('journey_BaseUrl_NoRealmPath_UsesRootAuthenticate', async () => { setupBaseUrlFetch(); - const client = await journey({ + const client = (await journey({ config: { serverConfig: { baseUrl } }, - }); + })) as JourneyClient; await client.start(); expect(mockFetch).toHaveBeenCalledTimes(1); @@ -594,9 +595,9 @@ describe('journey-client', () => { test('journey_BaseUrl_WithRealmPath_UsesRealmAuthenticate', async () => { setupBaseUrlFetch(); - const client = await journey({ + const client = (await journey({ config: { serverConfig: { baseUrl }, realmPath: 'alpha' }, - }); + })) as JourneyClient; await client.start(); expect(mockFetch).toHaveBeenCalledTimes(1); @@ -609,9 +610,9 @@ describe('journey-client', () => { test('journey_BaseUrl_TrailingSlashInput_NormalizesCorrectly', async () => { setupBaseUrlFetch(); - const client = await journey({ + const client = (await journey({ config: { serverConfig: { baseUrl: `${baseUrl}/` } }, - }); + })) as JourneyClient; await client.start(); const request = mockFetch.mock.calls[0][0] as Request; @@ -621,9 +622,9 @@ describe('journey-client', () => { test('journey_BaseUrl_Terminate_UsesRootSessionsUrl', async () => { setupBaseUrlFetch(); - const client = await journey({ + const client = (await journey({ config: { serverConfig: { baseUrl } }, - }); + })) as JourneyClient; await client.terminate(); expect(mockFetch).toHaveBeenCalledTimes(1); @@ -636,9 +637,9 @@ describe('journey-client', () => { test('journey_BaseUrl_WithRealmPath_Terminate_UsesRealmSessionsUrl', async () => { setupBaseUrlFetch(); - const client = await journey({ + const client = (await journey({ config: { serverConfig: { baseUrl }, realmPath: 'alpha' }, - }); + })) as JourneyClient; await client.terminate(); expect(mockFetch).toHaveBeenCalledTimes(1); diff --git a/packages/journey-client/src/lib/client.store.ts b/packages/journey-client/src/lib/client.store.ts index 0f26e34d294..2e58678cf15 100644 --- a/packages/journey-client/src/lib/client.store.ts +++ b/packages/journey-client/src/lib/client.store.ts @@ -6,6 +6,7 @@ */ import { logger as loggerFn } from '@forgerock/sdk-logger'; +import { assertValidStore, getClientForReducerPath, wellknownApi } from '@forgerock/sdk-store'; import { callbackType } from '@forgerock/sdk-types'; import { createWellknownError, @@ -16,14 +17,14 @@ import { import { createStorage } from '@forgerock/storage'; import * as Either from 'effect/Either'; -import { createJourneyStore } from './client.store.utils.js'; +import { createJourneyStore } from './client.store.effects.js'; import { configSlice } from './config.slice.js'; import { journeyApi } from './journey.api.js'; import { createJourneyObject, parseJourneyResponse } from './journey.utils.js'; -import { wellknownApi } from './wellknown.api.js'; import type { CustomLogger, LogLevel } from '@forgerock/sdk-logger'; import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; import type { GenericError } from '@forgerock/sdk-types'; import type { Step } from '@forgerock/sdk-types'; @@ -35,6 +36,7 @@ import type { JourneyStep } from './step.utils.js'; /** The journey client instance returned by the `journey()` function. */ export interface JourneyClient { + store: SdkStore; subscribe: (listener: () => void) => () => void; start: (options?: StartParam) => Promise; next: (step: JourneyStep, options?: NextOptions) => Promise; @@ -76,6 +78,7 @@ export async function journey({ config, requestMiddleware, logger, + store: sharedStore, }: { config: JourneyClientConfig; requestMiddleware?: RequestMiddleware[]; @@ -83,7 +86,12 @@ export async function journey({ level: LogLevel; custom?: CustomLogger; }; -}): Promise { + /** + * An existing SDK store to attach to, so discovery caching and state are + * shared with another client. Omit to create a store for this client alone. + */ + store?: unknown; +}): Promise { const log = loggerFn({ level: logger?.level ?? config.log ?? 'error', custom: logger?.custom, @@ -115,7 +123,24 @@ export async function journey({ ); } - const store = createJourneyStore({ requestMiddleware, logger: log }); + const storeError = assertValidStore(sharedStore); + if (storeError) return storeError; + + const validStore = sharedStore as SdkStore | undefined; + + if (validStore) { + const existing = getClientForReducerPath(validStore, journeyApi.reducerPath); + if (existing) { + return { + error: + 'This store already has a journey client attached. Use a separate store per journey client.', + type: 'argument_error' as const, + }; + } + } + + const handle = createJourneyStore({ requestMiddleware, logger: log, store: validStore }); + const store = handle.store; if ('baseUrl' in config.serverConfig) { const { baseUrl } = config.serverConfig; @@ -168,7 +193,7 @@ export async function journey({ ); } - const configError = store.getState().config.error; + const configError = store.getState().journeyConfig.error; if (configError) { const message = `${configError.error}: ${configError.message}`; @@ -182,6 +207,7 @@ export async function journey({ }); const self: JourneyClient = { + store: handle as SdkStore, subscribe: store.subscribe, start: async (options?: StartParam) => { diff --git a/packages/journey-client/src/lib/client.store.utils.ts b/packages/journey-client/src/lib/client.store.utils.ts index 6439d9a4a5a..2eae79f5277 100644 --- a/packages/journey-client/src/lib/client.store.utils.ts +++ b/packages/journey-client/src/lib/client.store.utils.ts @@ -5,43 +5,21 @@ * of the MIT license. See the LICENSE file for details. */ -import { combineReducers, configureStore } from '@reduxjs/toolkit'; +import { wellknownApi } from '@forgerock/sdk-store'; +import { combineSlices } from '@reduxjs/toolkit'; import { configSlice } from './config.slice.js'; import { journeyApi } from './journey.api.js'; -import { wellknownApi } from './wellknown.api.js'; -import type { logger as loggerFn } from '@forgerock/sdk-logger'; -import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; - -const rootReducer = combineReducers({ - [journeyApi.reducerPath]: journeyApi.reducer, - [configSlice.name]: configSlice.reducer, - [wellknownApi.reducerPath]: wellknownApi.reducer, -}); - -export const createJourneyStore = ({ - requestMiddleware, - logger, -}: { - requestMiddleware?: RequestMiddleware[]; - logger?: ReturnType; -}) => { - return configureStore({ - reducer: rootReducer, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - serializableCheck: true, - thunk: { - extraArgument: { - requestMiddleware, - logger, - }, - }, - }) - .concat(journeyApi.middleware) - .concat(wellknownApi.middleware), - }); -}; +/** + * The canonical description of the state this client contributes. + * + * The runtime store is assembled by `injectClient`, which TypeScript cannot + * follow across lazy injection. Combining the same slices here lets the state + * type be *derived* from them rather than hand-written, so it cannot drift from + * what is actually mounted. Exported so the derived state type resolves for + * consumers, and so an application can compose the reducer itself if it wants. + */ +export const rootReducer = combineSlices(journeyApi, configSlice, wellknownApi); export type RootState = ReturnType; diff --git a/packages/journey-client/src/lib/config.slice.ts b/packages/journey-client/src/lib/config.slice.ts index c6a7b23b6d0..727c993804f 100644 --- a/packages/journey-client/src/lib/config.slice.ts +++ b/packages/journey-client/src/lib/config.slice.ts @@ -46,7 +46,8 @@ const initialState: InternalJourneyClientConfig = { * and stores the resulting `ResolvedServerConfig`. */ export const configSlice = createSlice({ - name: 'config', + name: 'journeyConfig', + reducerPath: 'journeyConfig', initialState, reducers: { set(state, action: PayloadAction) { diff --git a/packages/journey-client/src/lib/journey.api.ts b/packages/journey-client/src/lib/journey.api.ts index 5ce1ce09c0b..ec412584a4e 100644 --- a/packages/journey-client/src/lib/journey.api.ts +++ b/packages/journey-client/src/lib/journey.api.ts @@ -5,11 +5,12 @@ * of the MIT license. See the LICENSE file for details. */ +import { logger as loggerFn } from '@forgerock/sdk-logger'; import { initQuery } from '@forgerock/sdk-request-middleware'; +import { clientExtra } from '@forgerock/sdk-store'; import { getEndpointPath, REQUESTED_WITH, resolve, stringify } from '@forgerock/sdk-utilities'; import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; -import type { logger as loggerFn } from '@forgerock/sdk-logger'; import type { RequestMiddleware } from '@forgerock/sdk-request-middleware'; import type { Step } from '@forgerock/sdk-types'; import type { @@ -27,10 +28,10 @@ import type { JourneyStep } from './step.types.js'; /** * Minimal state type for accessing journey config from RTK Query endpoints. - * References the config slice directly (not nested under journey). + * References the journey-specific config slice directly (not nested under journey). */ interface JourneyRootState { - config: InternalJourneyClientConfig; + journeyConfig: InternalJourneyClientConfig; } function constructUrl( @@ -84,13 +85,37 @@ function configureSessionRequest(): RequestInit { return init; } +const JOURNEY_REDUCER_PATH = 'journeyReducer'; + +/** + * This client's private slot on the store's `extraArgument`. + * + * Optional because a shared store may not have had a journey slot registered + * yet; `journeyExtra` substitutes safe defaults. + */ interface Extras { - requestMiddleware: RequestMiddleware[]; - logger: ReturnType; + requestMiddleware?: RequestMiddleware[]; + logger?: ReturnType; +} + +/** Creates an error-level fallback so a missing slot degrades safely, never crashes. */ +const createFallbackLogger = () => loggerFn({ level: 'error' }); + +/** + * Resolves this client's own middleware and logger. + * + * Reads only the `journeyReducer` slot — never a store-wide value, which on a + * shared store would belong to whichever client created it. + */ +function journeyExtra(extra: unknown): Required { + return clientExtra(extra, JOURNEY_REDUCER_PATH, { + requestMiddleware: [], + logger: createFallbackLogger(), + }); } export const journeyApi = createApi({ - reducerPath: 'journeyReducer', + reducerPath: JOURNEY_REDUCER_PATH, baseQuery: fetchBaseQuery({ baseUrl: '/', prepareHeaders: (headers: Headers) => { @@ -111,7 +136,7 @@ export const journeyApi = createApi({ baseQuery: BaseQueryFn, ) => { const state = api.getState() as JourneyRootState; - const { serverConfig } = state.config; + const { serverConfig } = state.journeyConfig; if (!serverConfig) { throw new Error('Server configuration is missing.'); } @@ -121,7 +146,7 @@ export const journeyApi = createApi({ const url = constructUrl(serverConfig, options?.journey, query); const request = configureRequest(); - const { requestMiddleware } = api.extra as Extras; + const { requestMiddleware } = journeyExtra(api.extra); const response = await initQuery({ ...request, url: url }, 'begin', { type: 'service', @@ -144,7 +169,7 @@ export const journeyApi = createApi({ baseQuery: BaseQueryFn, ) => { const state = api.getState() as JourneyRootState; - const { serverConfig } = state.config; + const { serverConfig } = state.journeyConfig; if (!serverConfig) { throw new Error('Server configuration is missing.'); } @@ -153,7 +178,7 @@ export const journeyApi = createApi({ const url = constructUrl(serverConfig, undefined, query); const request = configureRequest(step); - const { requestMiddleware } = api.extra as Extras; + const { requestMiddleware } = journeyExtra(api.extra); const response = await initQuery({ ...request, url }, 'continue') .applyMiddleware(requestMiddleware) @@ -173,7 +198,7 @@ export const journeyApi = createApi({ baseQuery: BaseQueryFn, ) => { const state = api.getState() as JourneyRootState; - const { serverConfig } = state.config; + const { serverConfig } = state.journeyConfig; if (!serverConfig) { throw new Error('Server configuration is missing.'); } @@ -183,7 +208,7 @@ export const journeyApi = createApi({ const url = constructSessionsUrl(serverConfig, query); const request = configureSessionRequest(); - const { requestMiddleware } = api.extra as Extras; + const { requestMiddleware } = journeyExtra(api.extra); const response = await initQuery({ ...request, url }, 'terminate') .applyMiddleware(requestMiddleware) diff --git a/packages/journey-client/src/lib/store-shape.test.ts b/packages/journey-client/src/lib/store-shape.test.ts new file mode 100644 index 00000000000..b6d6e6e1b3a --- /dev/null +++ b/packages/journey-client/src/lib/store-shape.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { describe, expect, it } from 'vitest'; + +import { createJourneyStore } from './client.store.effects.js'; + +/** + * `combineSlices` keys each reducer off `slice.reducerPath ?? slice.name`, where + * the previous `combineReducers({ ... })` form spelled the keys out literally. + * That makes the published state shape an implicit consequence of slice + * metadata: renaming `configSlice.name` would silently reshape the store. + * + * These assertions pin the shape so such a rename fails loudly here instead of + * in a consumer's selectors. + */ +describe('journey store shape', () => { + it('exposes exactly the expected top-level state keys', () => { + // Arrange + const { store } = createJourneyStore({}); + + // Act + const keys = Object.keys(store.getState()).sort(); + + // Assert + expect(keys).toEqual(['journeyConfig', 'journeyReducer', 'wellknown']); + }); + + it('registers this client\u2019s slot on the store extra, keyed by reducerPath', async () => { + // Arrange + const { store } = createJourneyStore({}); + let observed: unknown; + + // Act — a thunk is the supported way to observe extraArgument + await store.dispatch(((_dispatch: unknown, _getState: unknown, extra: unknown) => { + observed = extra; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }) as any); + + // Assert + expect(observed).toHaveProperty('clients.journeyReducer'); + }); +}); diff --git a/packages/journey-client/src/lib/wellknown.api.ts b/packages/journey-client/src/lib/wellknown.api.ts deleted file mode 100644 index ad89ff6cd68..00000000000 --- a/packages/journey-client/src/lib/wellknown.api.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ - -import { initWellknownQuery } from '@forgerock/sdk-oidc'; -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; - -import type { WellknownResponse } from '@forgerock/sdk-types'; -import type { - FetchBaseQueryError, - FetchBaseQueryMeta, - QueryReturnValue, -} from '@reduxjs/toolkit/query'; - -/** - * RTK Query API for well-known endpoint discovery. - * - * Uses the `initWellknownQuery` builder pattern from `@forgerock/sdk-oidc`. - * The builder constructs the request and validates the response; - * `fetchBaseQuery` handles the HTTP transport through RTK Query's pipeline. - */ -export const wellknownApi = createApi({ - reducerPath: 'wellknown', - baseQuery: fetchBaseQuery(), - endpoints: (builder) => ({ - configuration: builder.query({ - queryFn: async (url, _api, _extra, baseQuery) => { - const result = await initWellknownQuery(url).applyQuery(async (req) => { - const queryResult = await baseQuery(req); - return queryResult as QueryReturnValue; - }); - return result as QueryReturnValue< - WellknownResponse, - FetchBaseQueryError, - FetchBaseQueryMeta - >; - }, - }), - }), -}); diff --git a/packages/journey-client/tsconfig.lib.json b/packages/journey-client/tsconfig.lib.json index ca3f899b8dc..573c9f707ea 100644 --- a/packages/journey-client/tsconfig.lib.json +++ b/packages/journey-client/tsconfig.lib.json @@ -28,10 +28,10 @@ "path": "../sdk-types/tsconfig.lib.json" }, { - "path": "../sdk-effects/sdk-request-middleware/tsconfig.lib.json" + "path": "../sdk-effects/store/tsconfig.lib.json" }, { - "path": "../sdk-effects/oidc/tsconfig.lib.json" + "path": "../sdk-effects/sdk-request-middleware/tsconfig.lib.json" }, { "path": "../sdk-effects/logger/tsconfig.lib.json" diff --git a/packages/oidc-client/README.md b/packages/oidc-client/README.md index 4119d84729e..27bea7eb711 100644 --- a/packages/oidc-client/README.md +++ b/packages/oidc-client/README.md @@ -10,6 +10,7 @@ The oidc module follows the [OIDC](https://openid.net/specs/openid-connect-core- - [Initialization](#initialization) - [Configuration Options](#configuration-options) - [Quick Start](#quick-start) +- [Sharing a Store With Another Client](#sharing-a-store-with-another-client) - [API Reference](#api-reference) - [authorize](#authorize) - [token](#token) @@ -54,11 +55,17 @@ The `oidc()` initialization function accepts the following configuration: - **wellknown** (required) - URL to the OIDC provider's well-known configuration endpoint - **clientId** (required) - Your application's client ID registered with the OIDC provider - **redirectUri** (required) - The URI where the OIDC provider will redirect after authentication -- **scope** (required) - Space-separated list of requested scopes (e.g., `'openid profile email'`) +- **scope** (optional, default: `'openid'`) - Space-separated list of requested scopes (e.g., `'openid profile email'`) - **storage** (optional) - Storage configuration for tokens (defaults to localStorage) - **timeout** (optional) - Request timeout in milliseconds - **additionalParameters** (optional) - Additional parameters to include in authorization requests +The `oidc()` function also accepts: + +- **requestMiddleware** (optional) - Middleware applied to this client's requests only +- **logger** (optional) - Log level and custom logger for this client only +- **store** (optional) - An existing SDK store to attach to. See [Sharing a Store](#sharing-a-store-with-another-client) + ## Quick Start Here's a minimal example to get started: @@ -82,6 +89,65 @@ const user = await oidcClient.user.info(); await oidcClient.user.logout(); ``` +## Sharing a Store With Another Client + +If your application also uses `@forgerock/davinci-client` or `@forgerock/journey-client`, the clients can share one Redux store. The well-known discovery document is then fetched once rather than once per client. + +Pass the other client's `store` handle: + +```js +import { davinci } from '@forgerock/davinci-client'; +import { oidc } from '@forgerock/oidc-client'; + +const davinciClient = await davinci({ config: davinciConfig }); + +// Attaches to davinci's store; the discovery document is already cached there. +const oidcClient = await oidc({ config: oidcConfig, store: davinciClient.store }); +``` + +Or create the store yourself when neither client is the natural owner: + +```js +import { createSdkStore } from '@forgerock/sdk-store'; + +const store = createSdkStore(); +const davinciClient = await davinci({ config: davinciConfig, store }); +const oidcClient = await oidc({ config: oidcConfig, store }); +``` + +Omitting `store` is always valid — the client creates its own, which is the default behaviour. + +### Middleware and logging stay private + +Sharing a store shares cached data, not configuration. Your `requestMiddleware` and `logger` are registered against this client alone and are only applied to OIDC requests: + +```js +const oidcClient = await oidc({ + config, + store, + // Runs for AUTHORIZE, PAR, TOKEN_EXCHANGE, REVOKE, USER_INFO and END_SESSION only. + requestMiddleware: [myOidcMiddleware], + logger: { level: 'debug' }, +}); +``` + +Middleware passed to `davinci()` or `journey()` will never run against an OIDC token exchange, and middleware passed here will never run against their requests. + +### One OIDC client per store + +`oidc()` mounts at a fixed key in the store, so two OIDC clients sharing one store would overwrite each other's token state. Initialising a second client with a different `clientId` returns an `argument_error`: + +```js +const store = createSdkStore(); +await oidc({ config: { ...config, clientId: 'app-one' }, store }); + +const second = await oidc({ config: { ...config, clientId: 'app-two' }, store }); +// { error: "This store is already in use by an OIDC client with clientId 'app-one'. ...", +// type: 'argument_error' } +``` + +Re-initialising with the _same_ `clientId` is allowed and idempotent. If you need two clientIds, give each its own store. + ## API Reference ### authorize @@ -326,7 +392,7 @@ const tokens = await oidcClient.token.get({ if ('error' in tokens) { console.error('Failed to retrieve tokens:', tokens.error); } else { - console.log('Access token:', tokens.access_token); + console.log('Access token:', tokens.accessToken); } ``` diff --git a/packages/oidc-client/api-report/oidc-client.api.md b/packages/oidc-client/api-report/oidc-client.api.md index ec98f007181..3b609a9539e 100644 --- a/packages/oidc-client/api-report/oidc-client.api.md +++ b/packages/oidc-client/api-report/oidc-client.api.md @@ -1,668 +1,466 @@ -## API Report File for "@forgerock/oidc-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { BaseQueryFn } from '@reduxjs/toolkit/query'; -import { BrowserStorageConfig } from '@forgerock/storage'; -import { CombinedState } from '@reduxjs/toolkit/query'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import { CustomStorageConfig } from '@forgerock/storage'; -import { CustomStorageObject } from '@forgerock/sdk-types'; -import { EnhancedStore } from '@reduxjs/toolkit'; -import { FetchArgs } from '@reduxjs/toolkit/query'; -import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; -import { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; -import { GenericError } from '@forgerock/sdk-types'; -import { GetAuthorizationUrlOptions } from '@forgerock/sdk-types'; -import type { JWTPayload } from 'jose'; -import type { logger } from '@forgerock/sdk-logger'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { LogMessage } from '@forgerock/sdk-logger'; -import { makeOidcConfig } from '@forgerock/sdk-utilities'; -import { MutationDefinition } from '@reduxjs/toolkit/query'; -import { OidcConfig } from '@forgerock/sdk-types'; -import { QueryDefinition } from '@reduxjs/toolkit/query'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { ResponseType as ResponseType_2 } from '@forgerock/sdk-types'; -import { StorageConfig } from '@forgerock/storage'; -import { StoreEnhancer } from '@reduxjs/toolkit'; -import { ThunkDispatch } from '@reduxjs/toolkit'; -import { Tuple } from '@reduxjs/toolkit'; -import { UnknownAction } from '@reduxjs/toolkit'; -import { Unsubscribe } from '@reduxjs/toolkit'; -import { WellknownResponse } from '@forgerock/sdk-types'; - -export { ActionTypes }; - -// @public (undocumented) -export type AppDispatch = ReturnType; - -// @public (undocumented) -export interface AuthorizationError { - // (undocumented) - error: string; - // (undocumented) - error_description: string; - // (undocumented) - redirectUrl?: string; - // (undocumented) - type: 'auth_error' | 'argument_error' | 'network_error' | 'unknown_error' | 'wellknown_error'; -} - -// @public (undocumented) -export interface AuthorizationSuccess { - // (undocumented) - code: string; - // (undocumented) - state: string; -} - -// @public (undocumented) -export interface AuthorizeErrorResponse { - // (undocumented) - code?: string; - // (undocumented) - details?: [ - { - code: string; - message: string; - }, - ]; - // (undocumented) - id?: string; - // (undocumented) - message?: string; -} - -// @public (undocumented) -export interface AuthorizeSuccessResponse { - // (undocumented) - authorizeResponse?: { - code: string; - state: string; - }; - // (undocumented) - createdAt?: string; - // (undocumented) - _embedded?: { - [key: string]: unknown; - }; - // (undocumented) - environment?: { - id: string; - }; - // (undocumented) - expiresAt?: string; - // (undocumented) - id?: string; - // (undocumented) - _links?: { - [key: string]: { - href: string; - }; - }; - // (undocumented) - resumeUrl?: string; - // (undocumented) - session?: { - id: string; - }; - // (undocumented) - status?: string; -} - -export { BrowserStorageConfig }; - -// @public (undocumented) -export type BuildAuthorizationData = [string, GetAuthorizationUrlOptions]; - -// @public (undocumented) -export type ClientStore = ReturnType; - -// @public -export function createClientStore(input: { - requestMiddleware?: RequestMiddleware[]; - logger?: ReturnType; -}): EnhancedStore< - { - oidc: CombinedState< - { - authorizeFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn, - never, - AuthorizeSuccessResponse, - 'oidc', - unknown - >; - par: MutationDefinition< - { - endpoint: string; - body: URLSearchParams; - }, - BaseQueryFn, - never, - PushAuthorizationResponse, - 'oidc', - unknown - >; - sessionCheckIframe: MutationDefinition< - { - url: string; - responseType: SessionCheckResponseType; - }, - BaseQueryFn, - never, - { - params: Record; - }, - 'oidc', - unknown - >; - sessionCheckFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn, - never, - { - status: 204; - }, - 'oidc', - unknown - >; - authorizeIframe: MutationDefinition< - { - url: string; - }, - BaseQueryFn, - never, - AuthorizationSuccess, - 'oidc', - unknown - >; - endSession: MutationDefinition< - { - idToken: string; - endpoint: string; - signOutRedirectUri?: string; - }, - BaseQueryFn, - never, - null, - 'oidc', - unknown - >; - exchange: MutationDefinition< - { - code: string; - config: OidcConfig; - endpoint: string; - verifier?: string; - }, - BaseQueryFn, - never, - TokenExchangeResponse, - 'oidc', - unknown - >; - revoke: MutationDefinition< - { - accessToken: string; - clientId?: string; - endpoint: string; - }, - BaseQueryFn, - never, - object, - 'oidc', - unknown - >; - userInfo: MutationDefinition< - { - accessToken: string; - endpoint: string; - }, - BaseQueryFn, - never, - UserInfoResponse, - 'oidc', - unknown - >; - }, - never, - 'oidc' - >; - wellknown: CombinedState< - { - configuration: QueryDefinition< - string, - BaseQueryFn, - never, - WellknownResponse, - 'wellknown', - unknown - >; - }, - never, - 'wellknown' - >; - }, - UnknownAction, - Tuple< - [ - StoreEnhancer<{ - dispatch: ThunkDispatch< - { - oidc: CombinedState< - { - authorizeFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - AuthorizeSuccessResponse, - 'oidc', - unknown - >; - par: MutationDefinition< - { - endpoint: string; - body: URLSearchParams; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - PushAuthorizationResponse, - 'oidc', - unknown - >; - sessionCheckIframe: MutationDefinition< - { - url: string; - responseType: SessionCheckResponseType; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - { - params: Record; - }, - 'oidc', - unknown - >; - sessionCheckFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - { - status: 204; - }, - 'oidc', - unknown - >; - authorizeIframe: MutationDefinition< - { - url: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - AuthorizationSuccess, - 'oidc', - unknown - >; - endSession: MutationDefinition< - { - idToken: string; - endpoint: string; - signOutRedirectUri?: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - null, - 'oidc', - unknown - >; - exchange: MutationDefinition< - { - code: string; - config: OidcConfig; - endpoint: string; - verifier?: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - TokenExchangeResponse, - 'oidc', - unknown - >; - revoke: MutationDefinition< - { - accessToken: string; - clientId?: string; - endpoint: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - object, - 'oidc', - unknown - >; - userInfo: MutationDefinition< - { - accessToken: string; - endpoint: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - UserInfoResponse, - 'oidc', - unknown - >; - }, - never, - 'oidc' - >; - wellknown: CombinedState< - { - configuration: QueryDefinition< - string, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - WellknownResponse, - 'wellknown', - unknown - >; - }, - never, - 'wellknown' - >; - }, - { - requestMiddleware: RequestMiddleware[] | undefined; - logger: - | { - changeLevel: (level: LogLevel) => void; - error: (...args: LogMessage[]) => void; - warn: (...args: LogMessage[]) => void; - info: (...args: LogMessage[]) => void; - debug: (...args: LogMessage[]) => void; - } - | undefined; - }, - UnknownAction - >; - }>, - StoreEnhancer, - ] - > ->; - -export { CustomLogger }; - -export { CustomStorageConfig }; - -export { CustomStorageObject }; - -export { GenericError }; - -export { GetAuthorizationUrlOptions }; - -// @public (undocumented) -export interface GetTokensOptions { - // (undocumented) - authorizeOptions?: GetAuthorizationUrlOptions; - // (undocumented) - backgroundRenew?: boolean; - // (undocumented) - forceRenew?: boolean; - // (undocumented) - storageOptions?: Partial; -} - -export { LogLevel }; - -// @public (undocumented) -export type LogoutErrorResult = { - error: string; - sessionResponse: GenericError | null; - revokeResponse: GenericError | null; - deleteResponse: GenericError | null; -}; - -// @public (undocumented) -export type LogoutSuccessResult = RevokeSuccessResult & { - sessionResponse: null; -}; - -export { makeOidcConfig }; - -// @public (undocumented) -export interface OauthTokens { - // (undocumented) - accessToken: string; - // (undocumented) - expiresAt?: number; - // (undocumented) - expiryTimestamp?: number; - // (undocumented) - idToken: string; - // (undocumented) - refreshToken?: string; -} - -// @public -export function oidc(input: { - config: OidcConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; - storage?: Partial; -}): Promise< - | { - error: string; - type: string; - subscribe?: undefined; - authorize?: undefined; - token?: undefined; - user?: undefined; - } - | { - subscribe: (listener: () => void) => Unsubscribe; - authorize: { - url: (options?: GetAuthorizationUrlOptions) => Promise; - background: ( - options?: GetAuthorizationUrlOptions, - ) => Promise; - }; - token: { - exchange: ( - code: string, - state: string, - options?: Partial, - ) => Promise; - get: ( - options?: GetTokensOptions, - ) => Promise; - revoke: () => Promise; - }; - user: { - info: () => Promise; - logout: () => Promise; - session: (options?: SessionCheckOptions) => Promise; - }; - error?: undefined; - type?: undefined; - } ->; - -// @public (undocumented) -export type OidcClient = Awaited>; - -export { OidcConfig }; - -// @public (undocumented) -export type OptionalAuthorizeOptions = Partial; - -// @public (undocumented) -export interface PushAuthorizationResponse { - // (undocumented) - expires_in: number; - // (undocumented) - request_uri: string; -} - -export { RequestMiddleware }; - -export { ResponseType_2 as ResponseType }; - -// @public (undocumented) -export type RevokeErrorResult = { - error: string; - revokeResponse: GenericError | null; - deleteResponse: GenericError | null; -}; - -// @public (undocumented) -export type RevokeSuccessResult = { - revokeResponse: null; - deleteResponse: null; -}; - -// @public (undocumented) -export type RootState = ReturnType; - -// @public -export interface SessionCheckOptions { - redirectUri?: string; - responseType?: SessionCheckResponseType; - scope?: string; - subject?: string; -} - -// @public (undocumented) -export type SessionCheckResponseType = 'id_token' | 'none'; - -// @public (undocumented) -export type SessionCheckSuccess = { - responseType: SessionCheckResponseType; - claims?: JWTPayload; -}; - -export { StorageConfig }; - -// @public (undocumented) -export interface TokenExchangeErrorResponse { - // (undocumented) - error: string; - // (undocumented) - message: string; - // (undocumented) - type: 'exchange_error' | 'network_error' | 'state_error' | 'unknown_error'; -} - -// @public (undocumented) -export interface TokenExchangeResponse { - // (undocumented) - access_token: string; - // (undocumented) - expires_in?: number; - // (undocumented) - id_token: string; - // (undocumented) - refresh_token?: string; - // (undocumented) - scope?: string; - // (undocumented) - token_type?: string; -} - -// @public (undocumented) -export interface TokenRequestOptions { - // (undocumented) - code: string; - // (undocumented) - config: OidcConfig; - // (undocumented) - endpoint: string; - // (undocumented) - verifier?: string; -} - -// @public (undocumented) -export type UserInfoResponse = { - sub: string; - [key: string]: unknown; -}; - -export { WellknownResponse }; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/oidc-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { BaseQueryFn } from '@reduxjs/toolkit/query'; +import { BrowserStorageConfig } from '@forgerock/storage'; +import { CombinedSliceReducer } from '@reduxjs/toolkit'; +import { CombinedState } from '@reduxjs/toolkit/query'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import { CustomStorageConfig } from '@forgerock/storage'; +import { CustomStorageObject } from '@forgerock/sdk-types'; +import { FetchArgs } from '@reduxjs/toolkit/query'; +import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; +import { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; +import { GenericError } from '@forgerock/sdk-types'; +import { GetAuthorizationUrlOptions } from '@forgerock/sdk-types'; +import type { JWTPayload } from 'jose'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { makeOidcConfig } from '@forgerock/sdk-utilities'; +import { MutationDefinition } from '@reduxjs/toolkit/query'; +import { OidcConfig } from '@forgerock/sdk-types'; +import { QueryDefinition } from '@reduxjs/toolkit/query'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import { ResponseType as ResponseType_2 } from '@forgerock/sdk-types'; +import type { SdkStore } from '@forgerock/sdk-store'; +import type { SdkStoreHandle } from '@forgerock/sdk-store'; +import { StorageConfig } from '@forgerock/storage'; +import { Unsubscribe } from '@reduxjs/toolkit'; +import { WellknownResponse } from '@forgerock/sdk-types'; + +export { ActionTypes } + +// @public (undocumented) +export type AppDispatch = ReturnType; + +// @public (undocumented) +export interface AuthorizationError { + // (undocumented) + error: string; + // (undocumented) + error_description: string; + // (undocumented) + redirectUrl?: string; + // (undocumented) + type: 'auth_error' | 'argument_error' | 'network_error' | 'unknown_error' | 'wellknown_error'; +} + +// @public (undocumented) +export interface AuthorizationSuccess { + // (undocumented) + code: string; + // (undocumented) + state: string; +} + +// @public (undocumented) +export interface AuthorizeErrorResponse { + // (undocumented) + code?: string; + // (undocumented) + details?: [ + { + code: string; + message: string; + } + ]; + // (undocumented) + id?: string; + // (undocumented) + message?: string; +} + +// @public (undocumented) +export interface AuthorizeSuccessResponse { + // (undocumented) + authorizeResponse?: { + code: string; + state: string; + }; + // (undocumented) + createdAt?: string; + // (undocumented) + _embedded?: { + [key: string]: unknown; + }; + // (undocumented) + environment?: { + id: string; + }; + // (undocumented) + expiresAt?: string; + // (undocumented) + id?: string; + // (undocumented) + _links?: { + [key: string]: { + href: string; + }; + }; + // (undocumented) + resumeUrl?: string; + // (undocumented) + session?: { + id: string; + }; + // (undocumented) + status?: string; +} + +export { BrowserStorageConfig } + +// @public (undocumented) +export type BuildAuthorizationData = [string, GetAuthorizationUrlOptions]; + +// @public +export type ClientStore = SdkStoreHandle['store']; + +export { CustomLogger } + +export { CustomStorageConfig } + +export { CustomStorageObject } + +export { GenericError } + +export { GetAuthorizationUrlOptions } + +// @public (undocumented) +export interface GetTokensOptions { + // (undocumented) + authorizeOptions?: GetAuthorizationUrlOptions; + // (undocumented) + backgroundRenew?: boolean; + // (undocumented) + forceRenew?: boolean; + // (undocumented) + storageOptions?: Partial; +} + +export { LogLevel } + +// @public (undocumented) +export type LogoutErrorResult = { + error: string; + sessionResponse: GenericError | null; + revokeResponse: GenericError | null; + deleteResponse: GenericError | null; +}; + +// @public (undocumented) +export type LogoutSuccessResult = RevokeSuccessResult & { + sessionResponse: null; +}; + +export { makeOidcConfig } + +// @public (undocumented) +export interface OauthTokens { + // (undocumented) + accessToken: string; + // (undocumented) + expiresAt?: number; + // (undocumented) + expiryTimestamp?: number; + // (undocumented) + idToken: string; + // (undocumented) + refreshToken?: string; +} + +// @public +export function oidc(raw: RawOidcArgs): Promise void) => Unsubscribe; + authorize: { + url: (options?: GetAuthorizationUrlOptions) => Promise; + background: (options?: GetAuthorizationUrlOptions) => Promise; + }; + token: { + exchange: (code: string, state: string, options?: Partial) => Promise; + get: (options?: GetTokensOptions) => Promise; + revoke: () => Promise; + }; + user: { + info: () => Promise; + logout: () => Promise; + session: (options?: SessionCheckOptions) => Promise; + }; + error?: undefined; + type?: undefined; +}>; + +// @public (undocumented) +export type OidcClient = Awaited>; + +export { OidcConfig } + +// @public (undocumented) +export type OidcRootState = ReturnType; + +// @public (undocumented) +export type OptionalAuthorizeOptions = Partial; + +// @public (undocumented) +export interface PushAuthorizationResponse { + // (undocumented) + expires_in: number; + // (undocumented) + request_uri: string; +} + +// @public +export type RawOidcArgs = { + config: OidcConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; + storage?: Partial; + store?: unknown; +}; + +export { RequestMiddleware } + +export { ResponseType_2 as ResponseType } + +// @public (undocumented) +export type RevokeErrorResult = { + error: string; + revokeResponse: GenericError | null; + deleteResponse: GenericError | null; +}; + +// @public (undocumented) +export type RevokeSuccessResult = { + revokeResponse: null; + deleteResponse: null; +}; + +// @public +export const rootReducer: CombinedSliceReducer< { +oidc: CombinedState< { +authorizeFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizeSuccessResponse, "oidc", unknown>; +par: MutationDefinition< { +endpoint: string; +body: URLSearchParams; +}, BaseQueryFn, never, PushAuthorizationResponse, "oidc", unknown>; +sessionCheckIframe: MutationDefinition< { +url: string; +responseType: SessionCheckResponseType; +}, BaseQueryFn, never, { +params: Record; +}, "oidc", unknown>; +sessionCheckFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, { +status: 204; +}, "oidc", unknown>; +authorizeIframe: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizationSuccess, "oidc", unknown>; +endSession: MutationDefinition< { +idToken: string; +endpoint: string; +signOutRedirectUri?: string; +}, BaseQueryFn, never, null, "oidc", unknown>; +exchange: MutationDefinition< { +code: string; +config: OidcConfig; +endpoint: string; +verifier?: string; +}, BaseQueryFn, never, TokenExchangeResponse, "oidc", unknown>; +revoke: MutationDefinition< { +accessToken: string; +clientId?: string; +endpoint: string; +}, BaseQueryFn, never, object, "oidc", unknown>; +userInfo: MutationDefinition< { +accessToken: string; +endpoint: string; +}, BaseQueryFn, never, UserInfoResponse, "oidc", unknown>; +}, never, "oidc">; +wellknown: CombinedState< { +configuration: QueryDefinition, never, WellknownResponse, "wellknown", unknown>; +}, never, "wellknown">; +}, { +oidc: CombinedState< { +authorizeFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizeSuccessResponse, "oidc", unknown>; +par: MutationDefinition< { +endpoint: string; +body: URLSearchParams; +}, BaseQueryFn, never, PushAuthorizationResponse, "oidc", unknown>; +sessionCheckIframe: MutationDefinition< { +url: string; +responseType: SessionCheckResponseType; +}, BaseQueryFn, never, { +params: Record; +}, "oidc", unknown>; +sessionCheckFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, { +status: 204; +}, "oidc", unknown>; +authorizeIframe: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizationSuccess, "oidc", unknown>; +endSession: MutationDefinition< { +idToken: string; +endpoint: string; +signOutRedirectUri?: string; +}, BaseQueryFn, never, null, "oidc", unknown>; +exchange: MutationDefinition< { +code: string; +config: OidcConfig; +endpoint: string; +verifier?: string; +}, BaseQueryFn, never, TokenExchangeResponse, "oidc", unknown>; +revoke: MutationDefinition< { +accessToken: string; +clientId?: string; +endpoint: string; +}, BaseQueryFn, never, object, "oidc", unknown>; +userInfo: MutationDefinition< { +accessToken: string; +endpoint: string; +}, BaseQueryFn, never, UserInfoResponse, "oidc", unknown>; +}, never, "oidc">; +wellknown: CombinedState< { +configuration: QueryDefinition, never, WellknownResponse, "wellknown", unknown>; +}, never, "wellknown">; +}, Partial<{ +oidc: CombinedState< { +authorizeFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizeSuccessResponse, "oidc", unknown>; +par: MutationDefinition< { +endpoint: string; +body: URLSearchParams; +}, BaseQueryFn, never, PushAuthorizationResponse, "oidc", unknown>; +sessionCheckIframe: MutationDefinition< { +url: string; +responseType: SessionCheckResponseType; +}, BaseQueryFn, never, { +params: Record; +}, "oidc", unknown>; +sessionCheckFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, { +status: 204; +}, "oidc", unknown>; +authorizeIframe: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizationSuccess, "oidc", unknown>; +endSession: MutationDefinition< { +idToken: string; +endpoint: string; +signOutRedirectUri?: string; +}, BaseQueryFn, never, null, "oidc", unknown>; +exchange: MutationDefinition< { +code: string; +config: OidcConfig; +endpoint: string; +verifier?: string; +}, BaseQueryFn, never, TokenExchangeResponse, "oidc", unknown>; +revoke: MutationDefinition< { +accessToken: string; +clientId?: string; +endpoint: string; +}, BaseQueryFn, never, object, "oidc", unknown>; +userInfo: MutationDefinition< { +accessToken: string; +endpoint: string; +}, BaseQueryFn, never, UserInfoResponse, "oidc", unknown>; +}, never, "oidc">; +wellknown: CombinedState< { +configuration: QueryDefinition, never, WellknownResponse, "wellknown", unknown>; +}, never, "wellknown">; +}>>; + +// @public (undocumented) +export type RootState = ReturnType; + +// @public +export interface SessionCheckOptions { + redirectUri?: string; + responseType?: SessionCheckResponseType; + scope?: string; + subject?: string; +} + +// @public (undocumented) +export type SessionCheckResponseType = 'id_token' | 'none'; + +// @public (undocumented) +export type SessionCheckSuccess = { + responseType: SessionCheckResponseType; + claims?: JWTPayload; +}; + +export { StorageConfig } + +// @public (undocumented) +export interface TokenExchangeErrorResponse { + // (undocumented) + error: string; + // (undocumented) + message: string; + // (undocumented) + type: 'exchange_error' | 'network_error' | 'state_error' | 'unknown_error'; +} + +// @public (undocumented) +export interface TokenExchangeResponse { + // (undocumented) + access_token: string; + // (undocumented) + expires_in?: number; + // (undocumented) + id_token: string; + // (undocumented) + refresh_token?: string; + // (undocumented) + scope?: string; + // (undocumented) + token_type?: string; +} + +// @public (undocumented) +export interface TokenRequestOptions { + // (undocumented) + code: string; + // (undocumented) + config: OidcConfig; + // (undocumented) + endpoint: string; + // (undocumented) + verifier?: string; +} + +// @public (undocumented) +export type UserInfoResponse = { + sub: string; + [key: string]: unknown; +}; + +export { WellknownResponse } + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/oidc-client/api-report/oidc-client.types.api.md b/packages/oidc-client/api-report/oidc-client.types.api.md index 6157741a15d..c5cc4917889 100644 --- a/packages/oidc-client/api-report/oidc-client.types.api.md +++ b/packages/oidc-client/api-report/oidc-client.types.api.md @@ -1,665 +1,463 @@ -## API Report File for "@forgerock/oidc-client" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { ActionTypes } from '@forgerock/sdk-request-middleware'; -import { BaseQueryFn } from '@reduxjs/toolkit/query'; -import { BrowserStorageConfig } from '@forgerock/storage'; -import { CombinedState } from '@reduxjs/toolkit/query'; -import { CustomLogger } from '@forgerock/sdk-logger'; -import { CustomStorageConfig } from '@forgerock/storage'; -import { CustomStorageObject } from '@forgerock/sdk-types'; -import { EnhancedStore } from '@reduxjs/toolkit'; -import { FetchArgs } from '@reduxjs/toolkit/query'; -import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; -import { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; -import { GenericError } from '@forgerock/sdk-types'; -import { GetAuthorizationUrlOptions } from '@forgerock/sdk-types'; -import type { JWTPayload } from 'jose'; -import type { logger } from '@forgerock/sdk-logger'; -import { LogLevel } from '@forgerock/sdk-logger'; -import { LogMessage } from '@forgerock/sdk-logger'; -import { MutationDefinition } from '@reduxjs/toolkit/query'; -import { OidcConfig } from '@forgerock/sdk-types'; -import { QueryDefinition } from '@reduxjs/toolkit/query'; -import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; -import { ResponseType as ResponseType_2 } from '@forgerock/sdk-types'; -import { StorageConfig } from '@forgerock/storage'; -import { StoreEnhancer } from '@reduxjs/toolkit'; -import { ThunkDispatch } from '@reduxjs/toolkit'; -import { Tuple } from '@reduxjs/toolkit'; -import { UnknownAction } from '@reduxjs/toolkit'; -import { Unsubscribe } from '@reduxjs/toolkit'; -import { WellknownResponse } from '@forgerock/sdk-types'; - -export { ActionTypes }; - -// @public (undocumented) -export type AppDispatch = ReturnType; - -// @public (undocumented) -export interface AuthorizationError { - // (undocumented) - error: string; - // (undocumented) - error_description: string; - // (undocumented) - redirectUrl?: string; - // (undocumented) - type: 'auth_error' | 'argument_error' | 'network_error' | 'unknown_error' | 'wellknown_error'; -} - -// @public (undocumented) -export interface AuthorizationSuccess { - // (undocumented) - code: string; - // (undocumented) - state: string; -} - -// @public (undocumented) -export interface AuthorizeErrorResponse { - // (undocumented) - code?: string; - // (undocumented) - details?: [ - { - code: string; - message: string; - }, - ]; - // (undocumented) - id?: string; - // (undocumented) - message?: string; -} - -// @public (undocumented) -export interface AuthorizeSuccessResponse { - // (undocumented) - authorizeResponse?: { - code: string; - state: string; - }; - // (undocumented) - createdAt?: string; - // (undocumented) - _embedded?: { - [key: string]: unknown; - }; - // (undocumented) - environment?: { - id: string; - }; - // (undocumented) - expiresAt?: string; - // (undocumented) - id?: string; - // (undocumented) - _links?: { - [key: string]: { - href: string; - }; - }; - // (undocumented) - resumeUrl?: string; - // (undocumented) - session?: { - id: string; - }; - // (undocumented) - status?: string; -} - -export { BrowserStorageConfig }; - -// @public (undocumented) -export type BuildAuthorizationData = [string, GetAuthorizationUrlOptions]; - -// @public (undocumented) -export type ClientStore = ReturnType; - -// @public -export function createClientStore(input: { - requestMiddleware?: RequestMiddleware[]; - logger?: ReturnType; -}): EnhancedStore< - { - oidc: CombinedState< - { - authorizeFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn, - never, - AuthorizeSuccessResponse, - 'oidc', - unknown - >; - par: MutationDefinition< - { - endpoint: string; - body: URLSearchParams; - }, - BaseQueryFn, - never, - PushAuthorizationResponse, - 'oidc', - unknown - >; - sessionCheckIframe: MutationDefinition< - { - url: string; - responseType: SessionCheckResponseType; - }, - BaseQueryFn, - never, - { - params: Record; - }, - 'oidc', - unknown - >; - sessionCheckFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn, - never, - { - status: 204; - }, - 'oidc', - unknown - >; - authorizeIframe: MutationDefinition< - { - url: string; - }, - BaseQueryFn, - never, - AuthorizationSuccess, - 'oidc', - unknown - >; - endSession: MutationDefinition< - { - idToken: string; - endpoint: string; - signOutRedirectUri?: string; - }, - BaseQueryFn, - never, - null, - 'oidc', - unknown - >; - exchange: MutationDefinition< - { - code: string; - config: OidcConfig; - endpoint: string; - verifier?: string; - }, - BaseQueryFn, - never, - TokenExchangeResponse, - 'oidc', - unknown - >; - revoke: MutationDefinition< - { - accessToken: string; - clientId?: string; - endpoint: string; - }, - BaseQueryFn, - never, - object, - 'oidc', - unknown - >; - userInfo: MutationDefinition< - { - accessToken: string; - endpoint: string; - }, - BaseQueryFn, - never, - UserInfoResponse, - 'oidc', - unknown - >; - }, - never, - 'oidc' - >; - wellknown: CombinedState< - { - configuration: QueryDefinition< - string, - BaseQueryFn, - never, - WellknownResponse, - 'wellknown', - unknown - >; - }, - never, - 'wellknown' - >; - }, - UnknownAction, - Tuple< - [ - StoreEnhancer<{ - dispatch: ThunkDispatch< - { - oidc: CombinedState< - { - authorizeFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - AuthorizeSuccessResponse, - 'oidc', - unknown - >; - par: MutationDefinition< - { - endpoint: string; - body: URLSearchParams; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - PushAuthorizationResponse, - 'oidc', - unknown - >; - sessionCheckIframe: MutationDefinition< - { - url: string; - responseType: SessionCheckResponseType; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - { - params: Record; - }, - 'oidc', - unknown - >; - sessionCheckFetch: MutationDefinition< - { - url: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - { - status: 204; - }, - 'oidc', - unknown - >; - authorizeIframe: MutationDefinition< - { - url: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - AuthorizationSuccess, - 'oidc', - unknown - >; - endSession: MutationDefinition< - { - idToken: string; - endpoint: string; - signOutRedirectUri?: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - null, - 'oidc', - unknown - >; - exchange: MutationDefinition< - { - code: string; - config: OidcConfig; - endpoint: string; - verifier?: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - TokenExchangeResponse, - 'oidc', - unknown - >; - revoke: MutationDefinition< - { - accessToken: string; - clientId?: string; - endpoint: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - object, - 'oidc', - unknown - >; - userInfo: MutationDefinition< - { - accessToken: string; - endpoint: string; - }, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - UserInfoResponse, - 'oidc', - unknown - >; - }, - never, - 'oidc' - >; - wellknown: CombinedState< - { - configuration: QueryDefinition< - string, - BaseQueryFn< - string | FetchArgs, - unknown, - FetchBaseQueryError, - {}, - FetchBaseQueryMeta - >, - never, - WellknownResponse, - 'wellknown', - unknown - >; - }, - never, - 'wellknown' - >; - }, - { - requestMiddleware: RequestMiddleware[] | undefined; - logger: - | { - changeLevel: (level: LogLevel) => void; - error: (...args: LogMessage[]) => void; - warn: (...args: LogMessage[]) => void; - info: (...args: LogMessage[]) => void; - debug: (...args: LogMessage[]) => void; - } - | undefined; - }, - UnknownAction - >; - }>, - StoreEnhancer, - ] - > ->; - -export { CustomLogger }; - -export { CustomStorageConfig }; - -export { CustomStorageObject }; - -export { GenericError }; - -export { GetAuthorizationUrlOptions }; - -// @public (undocumented) -export interface GetTokensOptions { - // (undocumented) - authorizeOptions?: GetAuthorizationUrlOptions; - // (undocumented) - backgroundRenew?: boolean; - // (undocumented) - forceRenew?: boolean; - // (undocumented) - storageOptions?: Partial; -} - -export { LogLevel }; - -// @public (undocumented) -export type LogoutErrorResult = { - error: string; - sessionResponse: GenericError | null; - revokeResponse: GenericError | null; - deleteResponse: GenericError | null; -}; - -// @public (undocumented) -export type LogoutSuccessResult = RevokeSuccessResult & { - sessionResponse: null; -}; - -// @public (undocumented) -export interface OauthTokens { - // (undocumented) - accessToken: string; - // (undocumented) - expiresAt?: number; - // (undocumented) - expiryTimestamp?: number; - // (undocumented) - idToken: string; - // (undocumented) - refreshToken?: string; -} - -// @public -export function oidc(input: { - config: OidcConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; - storage?: Partial; -}): Promise< - | { - error: string; - type: string; - subscribe?: undefined; - authorize?: undefined; - token?: undefined; - user?: undefined; - } - | { - subscribe: (listener: () => void) => Unsubscribe; - authorize: { - url: (options?: GetAuthorizationUrlOptions) => Promise; - background: ( - options?: GetAuthorizationUrlOptions, - ) => Promise; - }; - token: { - exchange: ( - code: string, - state: string, - options?: Partial, - ) => Promise; - get: ( - options?: GetTokensOptions, - ) => Promise; - revoke: () => Promise; - }; - user: { - info: () => Promise; - logout: () => Promise; - session: (options?: SessionCheckOptions) => Promise; - }; - error?: undefined; - type?: undefined; - } ->; - -// @public (undocumented) -export type OidcClient = Awaited>; - -export { OidcConfig }; - -// @public (undocumented) -export type OptionalAuthorizeOptions = Partial; - -// @public (undocumented) -export interface PushAuthorizationResponse { - // (undocumented) - expires_in: number; - // (undocumented) - request_uri: string; -} - -export { RequestMiddleware }; - -export { ResponseType_2 as ResponseType }; - -// @public (undocumented) -export type RevokeErrorResult = { - error: string; - revokeResponse: GenericError | null; - deleteResponse: GenericError | null; -}; - -// @public (undocumented) -export type RevokeSuccessResult = { - revokeResponse: null; - deleteResponse: null; -}; - -// @public (undocumented) -export type RootState = ReturnType; - -// @public -export interface SessionCheckOptions { - redirectUri?: string; - responseType?: SessionCheckResponseType; - scope?: string; - subject?: string; -} - -// @public (undocumented) -export type SessionCheckResponseType = 'id_token' | 'none'; - -// @public (undocumented) -export type SessionCheckSuccess = { - responseType: SessionCheckResponseType; - claims?: JWTPayload; -}; - -export { StorageConfig }; - -// @public (undocumented) -export interface TokenExchangeErrorResponse { - // (undocumented) - error: string; - // (undocumented) - message: string; - // (undocumented) - type: 'exchange_error' | 'network_error' | 'state_error' | 'unknown_error'; -} - -// @public (undocumented) -export interface TokenExchangeResponse { - // (undocumented) - access_token: string; - // (undocumented) - expires_in?: number; - // (undocumented) - id_token: string; - // (undocumented) - refresh_token?: string; - // (undocumented) - scope?: string; - // (undocumented) - token_type?: string; -} - -// @public (undocumented) -export interface TokenRequestOptions { - // (undocumented) - code: string; - // (undocumented) - config: OidcConfig; - // (undocumented) - endpoint: string; - // (undocumented) - verifier?: string; -} - -// @public (undocumented) -export type UserInfoResponse = { - sub: string; - [key: string]: unknown; -}; - -export { WellknownResponse }; - -// (No @packageDocumentation comment for this package) -``` +## API Report File for "@forgerock/oidc-client" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import { ActionTypes } from '@forgerock/sdk-request-middleware'; +import { BaseQueryFn } from '@reduxjs/toolkit/query'; +import { BrowserStorageConfig } from '@forgerock/storage'; +import { CombinedSliceReducer } from '@reduxjs/toolkit'; +import { CombinedState } from '@reduxjs/toolkit/query'; +import { CustomLogger } from '@forgerock/sdk-logger'; +import { CustomStorageConfig } from '@forgerock/storage'; +import { CustomStorageObject } from '@forgerock/sdk-types'; +import { FetchArgs } from '@reduxjs/toolkit/query'; +import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; +import { FetchBaseQueryMeta } from '@reduxjs/toolkit/query'; +import { GenericError } from '@forgerock/sdk-types'; +import { GetAuthorizationUrlOptions } from '@forgerock/sdk-types'; +import type { JWTPayload } from 'jose'; +import { LogLevel } from '@forgerock/sdk-logger'; +import { MutationDefinition } from '@reduxjs/toolkit/query'; +import { OidcConfig } from '@forgerock/sdk-types'; +import { QueryDefinition } from '@reduxjs/toolkit/query'; +import { RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import { ResponseType as ResponseType_2 } from '@forgerock/sdk-types'; +import type { SdkStore } from '@forgerock/sdk-store'; +import type { SdkStoreHandle } from '@forgerock/sdk-store'; +import { StorageConfig } from '@forgerock/storage'; +import { Unsubscribe } from '@reduxjs/toolkit'; +import { WellknownResponse } from '@forgerock/sdk-types'; + +export { ActionTypes } + +// @public (undocumented) +export type AppDispatch = ReturnType; + +// @public (undocumented) +export interface AuthorizationError { + // (undocumented) + error: string; + // (undocumented) + error_description: string; + // (undocumented) + redirectUrl?: string; + // (undocumented) + type: 'auth_error' | 'argument_error' | 'network_error' | 'unknown_error' | 'wellknown_error'; +} + +// @public (undocumented) +export interface AuthorizationSuccess { + // (undocumented) + code: string; + // (undocumented) + state: string; +} + +// @public (undocumented) +export interface AuthorizeErrorResponse { + // (undocumented) + code?: string; + // (undocumented) + details?: [ + { + code: string; + message: string; + } + ]; + // (undocumented) + id?: string; + // (undocumented) + message?: string; +} + +// @public (undocumented) +export interface AuthorizeSuccessResponse { + // (undocumented) + authorizeResponse?: { + code: string; + state: string; + }; + // (undocumented) + createdAt?: string; + // (undocumented) + _embedded?: { + [key: string]: unknown; + }; + // (undocumented) + environment?: { + id: string; + }; + // (undocumented) + expiresAt?: string; + // (undocumented) + id?: string; + // (undocumented) + _links?: { + [key: string]: { + href: string; + }; + }; + // (undocumented) + resumeUrl?: string; + // (undocumented) + session?: { + id: string; + }; + // (undocumented) + status?: string; +} + +export { BrowserStorageConfig } + +// @public (undocumented) +export type BuildAuthorizationData = [string, GetAuthorizationUrlOptions]; + +// @public +export type ClientStore = SdkStoreHandle['store']; + +export { CustomLogger } + +export { CustomStorageConfig } + +export { CustomStorageObject } + +export { GenericError } + +export { GetAuthorizationUrlOptions } + +// @public (undocumented) +export interface GetTokensOptions { + // (undocumented) + authorizeOptions?: GetAuthorizationUrlOptions; + // (undocumented) + backgroundRenew?: boolean; + // (undocumented) + forceRenew?: boolean; + // (undocumented) + storageOptions?: Partial; +} + +export { LogLevel } + +// @public (undocumented) +export type LogoutErrorResult = { + error: string; + sessionResponse: GenericError | null; + revokeResponse: GenericError | null; + deleteResponse: GenericError | null; +}; + +// @public (undocumented) +export type LogoutSuccessResult = RevokeSuccessResult & { + sessionResponse: null; +}; + +// @public (undocumented) +export interface OauthTokens { + // (undocumented) + accessToken: string; + // (undocumented) + expiresAt?: number; + // (undocumented) + expiryTimestamp?: number; + // (undocumented) + idToken: string; + // (undocumented) + refreshToken?: string; +} + +// @public +export function oidc(raw: RawOidcArgs): Promise void) => Unsubscribe; + authorize: { + url: (options?: GetAuthorizationUrlOptions) => Promise; + background: (options?: GetAuthorizationUrlOptions) => Promise; + }; + token: { + exchange: (code: string, state: string, options?: Partial) => Promise; + get: (options?: GetTokensOptions) => Promise; + revoke: () => Promise; + }; + user: { + info: () => Promise; + logout: () => Promise; + session: (options?: SessionCheckOptions) => Promise; + }; + error?: undefined; + type?: undefined; +}>; + +// @public (undocumented) +export type OidcClient = Awaited>; + +export { OidcConfig } + +// @public (undocumented) +export type OidcRootState = ReturnType; + +// @public (undocumented) +export type OptionalAuthorizeOptions = Partial; + +// @public (undocumented) +export interface PushAuthorizationResponse { + // (undocumented) + expires_in: number; + // (undocumented) + request_uri: string; +} + +// @public +export type RawOidcArgs = { + config: OidcConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { + level: LogLevel; + custom?: CustomLogger; + }; + storage?: Partial; + store?: unknown; +}; + +export { RequestMiddleware } + +export { ResponseType_2 as ResponseType } + +// @public (undocumented) +export type RevokeErrorResult = { + error: string; + revokeResponse: GenericError | null; + deleteResponse: GenericError | null; +}; + +// @public (undocumented) +export type RevokeSuccessResult = { + revokeResponse: null; + deleteResponse: null; +}; + +// @public +export const rootReducer: CombinedSliceReducer< { +oidc: CombinedState< { +authorizeFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizeSuccessResponse, "oidc", unknown>; +par: MutationDefinition< { +endpoint: string; +body: URLSearchParams; +}, BaseQueryFn, never, PushAuthorizationResponse, "oidc", unknown>; +sessionCheckIframe: MutationDefinition< { +url: string; +responseType: SessionCheckResponseType; +}, BaseQueryFn, never, { +params: Record; +}, "oidc", unknown>; +sessionCheckFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, { +status: 204; +}, "oidc", unknown>; +authorizeIframe: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizationSuccess, "oidc", unknown>; +endSession: MutationDefinition< { +idToken: string; +endpoint: string; +signOutRedirectUri?: string; +}, BaseQueryFn, never, null, "oidc", unknown>; +exchange: MutationDefinition< { +code: string; +config: OidcConfig; +endpoint: string; +verifier?: string; +}, BaseQueryFn, never, TokenExchangeResponse, "oidc", unknown>; +revoke: MutationDefinition< { +accessToken: string; +clientId?: string; +endpoint: string; +}, BaseQueryFn, never, object, "oidc", unknown>; +userInfo: MutationDefinition< { +accessToken: string; +endpoint: string; +}, BaseQueryFn, never, UserInfoResponse, "oidc", unknown>; +}, never, "oidc">; +wellknown: CombinedState< { +configuration: QueryDefinition, never, WellknownResponse, "wellknown", unknown>; +}, never, "wellknown">; +}, { +oidc: CombinedState< { +authorizeFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizeSuccessResponse, "oidc", unknown>; +par: MutationDefinition< { +endpoint: string; +body: URLSearchParams; +}, BaseQueryFn, never, PushAuthorizationResponse, "oidc", unknown>; +sessionCheckIframe: MutationDefinition< { +url: string; +responseType: SessionCheckResponseType; +}, BaseQueryFn, never, { +params: Record; +}, "oidc", unknown>; +sessionCheckFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, { +status: 204; +}, "oidc", unknown>; +authorizeIframe: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizationSuccess, "oidc", unknown>; +endSession: MutationDefinition< { +idToken: string; +endpoint: string; +signOutRedirectUri?: string; +}, BaseQueryFn, never, null, "oidc", unknown>; +exchange: MutationDefinition< { +code: string; +config: OidcConfig; +endpoint: string; +verifier?: string; +}, BaseQueryFn, never, TokenExchangeResponse, "oidc", unknown>; +revoke: MutationDefinition< { +accessToken: string; +clientId?: string; +endpoint: string; +}, BaseQueryFn, never, object, "oidc", unknown>; +userInfo: MutationDefinition< { +accessToken: string; +endpoint: string; +}, BaseQueryFn, never, UserInfoResponse, "oidc", unknown>; +}, never, "oidc">; +wellknown: CombinedState< { +configuration: QueryDefinition, never, WellknownResponse, "wellknown", unknown>; +}, never, "wellknown">; +}, Partial<{ +oidc: CombinedState< { +authorizeFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizeSuccessResponse, "oidc", unknown>; +par: MutationDefinition< { +endpoint: string; +body: URLSearchParams; +}, BaseQueryFn, never, PushAuthorizationResponse, "oidc", unknown>; +sessionCheckIframe: MutationDefinition< { +url: string; +responseType: SessionCheckResponseType; +}, BaseQueryFn, never, { +params: Record; +}, "oidc", unknown>; +sessionCheckFetch: MutationDefinition< { +url: string; +}, BaseQueryFn, never, { +status: 204; +}, "oidc", unknown>; +authorizeIframe: MutationDefinition< { +url: string; +}, BaseQueryFn, never, AuthorizationSuccess, "oidc", unknown>; +endSession: MutationDefinition< { +idToken: string; +endpoint: string; +signOutRedirectUri?: string; +}, BaseQueryFn, never, null, "oidc", unknown>; +exchange: MutationDefinition< { +code: string; +config: OidcConfig; +endpoint: string; +verifier?: string; +}, BaseQueryFn, never, TokenExchangeResponse, "oidc", unknown>; +revoke: MutationDefinition< { +accessToken: string; +clientId?: string; +endpoint: string; +}, BaseQueryFn, never, object, "oidc", unknown>; +userInfo: MutationDefinition< { +accessToken: string; +endpoint: string; +}, BaseQueryFn, never, UserInfoResponse, "oidc", unknown>; +}, never, "oidc">; +wellknown: CombinedState< { +configuration: QueryDefinition, never, WellknownResponse, "wellknown", unknown>; +}, never, "wellknown">; +}>>; + +// @public (undocumented) +export type RootState = ReturnType; + +// @public +export interface SessionCheckOptions { + redirectUri?: string; + responseType?: SessionCheckResponseType; + scope?: string; + subject?: string; +} + +// @public (undocumented) +export type SessionCheckResponseType = 'id_token' | 'none'; + +// @public (undocumented) +export type SessionCheckSuccess = { + responseType: SessionCheckResponseType; + claims?: JWTPayload; +}; + +export { StorageConfig } + +// @public (undocumented) +export interface TokenExchangeErrorResponse { + // (undocumented) + error: string; + // (undocumented) + message: string; + // (undocumented) + type: 'exchange_error' | 'network_error' | 'state_error' | 'unknown_error'; +} + +// @public (undocumented) +export interface TokenExchangeResponse { + // (undocumented) + access_token: string; + // (undocumented) + expires_in?: number; + // (undocumented) + id_token: string; + // (undocumented) + refresh_token?: string; + // (undocumented) + scope?: string; + // (undocumented) + token_type?: string; +} + +// @public (undocumented) +export interface TokenRequestOptions { + // (undocumented) + code: string; + // (undocumented) + config: OidcConfig; + // (undocumented) + endpoint: string; + // (undocumented) + verifier?: string; +} + +// @public (undocumented) +export type UserInfoResponse = { + sub: string; + [key: string]: unknown; +}; + +export { WellknownResponse } + +// (No @packageDocumentation comment for this package) + +``` diff --git a/packages/oidc-client/package.json b/packages/oidc-client/package.json index 93e7dd09676..e6c175bdc85 100644 --- a/packages/oidc-client/package.json +++ b/packages/oidc-client/package.json @@ -31,6 +31,7 @@ "@forgerock/sdk-logger": "workspace:*", "@forgerock/sdk-oidc": "workspace:*", "@forgerock/sdk-request-middleware": "workspace:*", + "@forgerock/sdk-store": "workspace:*", "@forgerock/sdk-types": "workspace:*", "@forgerock/sdk-utilities": "workspace:*", "@forgerock/storage": "workspace:*", diff --git a/packages/oidc-client/src/lib/client-extra.test.ts b/packages/oidc-client/src/lib/client-extra.test.ts new file mode 100644 index 00000000000..493767e60ed --- /dev/null +++ b/packages/oidc-client/src/lib/client-extra.test.ts @@ -0,0 +1,192 @@ +// @vitest-environment node +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { wellknownApi } from '@forgerock/sdk-store'; +import { configureStore } from '@reduxjs/toolkit'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { oidcApi } from './oidc.api.js'; + +import type { RequestMiddleware } from '@forgerock/sdk-request-middleware'; + +/** + * Regression coverage for the shared-store middleware leak. + * + * OIDC endpoints resolve their middleware and logger from the store's thunk + * `extraArgument`. When a store is shared with davinci/journey, that `extra` + * belongs to the owning client — so before the per-client registry, DaVinci + * middleware executed against TOKEN_EXCHANGE, REVOKE, END_SESSION and friends, + * and oidc's own logger was silently discarded. + * + * These tests build a store whose `extra` carries slots for two clients and + * assert that oidc endpoints only ever see their own. + */ + +const REVOKE_URL = 'https://example.pingone.com/test-env/as/revoke'; + +function recordingMiddleware(calls: string[], label: string): RequestMiddleware { + return (_req, action, next) => { + calls.push(`${label}:${action.type}`); + next(); + }; +} + +function makeLoggerSpy() { + return { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; +} + +/** A store shaped like a shared SDK store: one `extra`, one slot per client. */ +function makeTwoClientStore(slots: Record) { + return configureStore({ + reducer: { + [oidcApi.reducerPath]: oidcApi.reducer, + [wellknownApi.reducerPath]: wellknownApi.reducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ thunk: { extraArgument: { clients: slots } } }) + .concat(wellknownApi.middleware) + .concat(oidcApi.middleware), + }); +} + +describe('oidc endpoints resolve middleware and logger per client', () => { + beforeEach(() => { + vi.spyOn(globalThis, 'fetch').mockImplementation( + async () => + new Response(JSON.stringify({ access_token: 'at', token_type: 'Bearer' }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }), + ); + }); + + afterEach(() => { + vi.restoreAllMocks(); + oidcApi.util.resetApiState(); + }); + + it('does not run another client\u2019s requestMiddleware against an OIDC request', async () => { + // Arrange + const calls: string[] = []; + const store = makeTwoClientStore({ + davinci: { + requestMiddleware: [recordingMiddleware(calls, 'davinci')], + logger: makeLoggerSpy(), + }, + oidc: { requestMiddleware: [], logger: makeLoggerSpy() }, + }); + + // Act + await store.dispatch( + oidcApi.endpoints.revoke.initiate({ + accessToken: 'test-access-token', + clientId: 'test-client-id', + endpoint: REVOKE_URL, + }), + ); + + // Assert — before the per-client registry this recorded 'davinci:TOKEN_EXCHANGE' + expect(calls).toEqual([]); + }); + + it("runs the OIDC client's own requestMiddleware against an OIDC request", async () => { + // Arrange + const calls: string[] = []; + const store = makeTwoClientStore({ + davinci: { requestMiddleware: [recordingMiddleware(calls, 'davinci')] }, + oidc: { + requestMiddleware: [recordingMiddleware(calls, 'oidc')], + logger: makeLoggerSpy(), + }, + }); + + // Act + await store.dispatch( + oidcApi.endpoints.revoke.initiate({ + accessToken: 'test-access-token', + clientId: 'test-client-id', + endpoint: REVOKE_URL, + }), + ); + + // Assert + expect(calls.filter((c) => c.startsWith('oidc:'))).not.toHaveLength(0); + expect(calls.filter((c) => c.startsWith('davinci:'))).toHaveLength(0); + }); + + it('ignores a store-wide flat middleware list (the original leak shape)', async () => { + // Arrange — this is exactly what davinci/journey used to put in extraArgument. + // If oidc ever reads the whole `extra` again instead of its own slot, this fails. + const calls: string[] = []; + const store = configureStore({ + reducer: { + [oidcApi.reducerPath]: oidcApi.reducer, + [wellknownApi.reducerPath]: wellknownApi.reducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + thunk: { + extraArgument: { + requestMiddleware: [recordingMiddleware(calls, 'store-wide')], + logger: makeLoggerSpy(), + }, + }, + }) + .concat(wellknownApi.middleware) + .concat(oidcApi.middleware), + }); + + // Act + await store.dispatch( + oidcApi.endpoints.revoke.initiate({ + accessToken: 'test-access-token', + clientId: 'test-client-id', + endpoint: REVOKE_URL, + }), + ); + + // Assert + expect(calls).toEqual([]); + }); + + it("uses the OIDC client's own logger, not the owning client's", async () => { + // Arrange + const oidcLogger = makeLoggerSpy(); + const davinciLogger = makeLoggerSpy(); + const store = makeTwoClientStore({ + davinci: { requestMiddleware: [], logger: davinciLogger }, + oidc: { requestMiddleware: [], logger: oidcLogger }, + }); + + // Act + await store.dispatch( + oidcApi.endpoints.revoke.initiate({ + accessToken: 'test-access-token', + clientId: 'test-client-id', + endpoint: REVOKE_URL, + }), + ); + + // Assert + const oidcCalls = + oidcLogger.debug.mock.calls.length + + oidcLogger.info.mock.calls.length + + oidcLogger.error.mock.calls.length; + const davinciCalls = + davinciLogger.debug.mock.calls.length + + davinciLogger.info.mock.calls.length + + davinciLogger.error.mock.calls.length; + + expect(oidcCalls).toBeGreaterThan(0); + expect(davinciCalls).toBe(0); + }); +}); diff --git a/packages/oidc-client/src/lib/client.store.effects.ts b/packages/oidc-client/src/lib/client.store.effects.ts new file mode 100644 index 00000000000..e96fff4fb00 --- /dev/null +++ b/packages/oidc-client/src/lib/client.store.effects.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { createSdkStore, getClientForReducerPath, injectClient } from '@forgerock/sdk-store'; + +import { oidcApi } from './oidc.api.js'; + +import type { logger as loggerFn } from '@forgerock/sdk-logger'; +import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore, SdkStoreHandle } from '@forgerock/sdk-store'; + +import type { OidcRootState } from './client.store.utils.js'; + +/** + * Creates or attaches the private store backing an OIDC client after the + * public factory has validated its arguments and shared-store ownership. + */ +export function createClientStore({ + requestMiddleware, + logger, + store, + clientId, +}: { + requestMiddleware?: RequestMiddleware[]; + logger?: ReturnType; + store?: SdkStore; + clientId?: string; +}): SdkStoreHandle { + const conflict = store && getClientForReducerPath(store, oidcApi.reducerPath)?.clientId; + if (conflict && conflict !== clientId) { + throw new Error( + `This store is already in use by an OIDC client with clientId '${conflict}'. ` + + 'Use a separate store per clientId.', + ); + } + + return injectClient(store ?? createSdkStore(), { + api: oidcApi, + requestMiddleware, + logger, + clientId, + }); +} diff --git a/packages/oidc-client/src/lib/client.store.test.ts b/packages/oidc-client/src/lib/client.store.test.ts index 64d5b6695c8..4991f9bc9a6 100644 --- a/packages/oidc-client/src/lib/client.store.test.ts +++ b/packages/oidc-client/src/lib/client.store.test.ts @@ -5,6 +5,7 @@ * of the MIT license. See the LICENSE file for details. */ +import { createSdkStore } from '@forgerock/sdk-store'; import { makeOidcConfig } from '@forgerock/sdk-utilities'; import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node'; @@ -959,6 +960,44 @@ describe('unified JSON config entry', () => { }); }); +describe('shared wellknown cache', () => { + const config: OidcConfig = { + clientId: 'shared-client', + redirectUri: 'https://example.com/callback.html', + scope: 'openid profile', + serverConfig: { wellknown: 'https://api.example.com/wellknown' }, + responseType: 'code', + }; + + it('clears a failed discovery query so a second client can retry', async () => { + const sharedStore = createSdkStore(); + server.use( + http.get('*/wellknown', () => HttpResponse.json({ error: 'unavailable' }, { status: 500 })), + ); + + const first = await oidc({ config, storage: customStorageConfig, store: sharedStore }); + expect(first).toMatchObject({ type: 'wellknown_error' }); + + server.use( + http.get('*/wellknown', () => + HttpResponse.json({ + issuer: 'https://api.example.com/as/issuer', + authorization_endpoint: 'https://api.example.com/as/authorize', + token_endpoint: 'https://api.example.com/as/token', + userinfo_endpoint: 'https://api.example.com/as/userinfo', + introspection_endpoint: 'https://api.example.com/as/introspect', + revocation_endpoint: 'https://api.example.com/as/revoke', + response_types_supported: ['code'], + response_modes_supported: ['query'], + }), + ), + ); + + const second = await oidc({ config, storage: customStorageConfig, store: sharedStore }); + expect(second).not.toHaveProperty('error'); + }); +}); + describe('user.session()', async () => { const config: OidcConfig = { clientId: '123456789', diff --git a/packages/oidc-client/src/lib/client.store.ts b/packages/oidc-client/src/lib/client.store.ts index e971a35ea7d..ce1226f93c2 100644 --- a/packages/oidc-client/src/lib/client.store.ts +++ b/packages/oidc-client/src/lib/client.store.ts @@ -6,26 +6,28 @@ */ import { logger as loggerFn } from '@forgerock/sdk-logger'; import { createAuthorizeUrl } from '@forgerock/sdk-oidc'; +import { createSdkStore, wellknownApi, wellknownSelector } from '@forgerock/sdk-store'; import { handleMicroExit } from '@forgerock/sdk-utilities'; import { createStorage } from '@forgerock/storage'; import { Micro } from 'effect'; import { causeIsDie, exitIsFail, exitIsSuccess } from 'effect/Micro'; import { authorizeµ, createParAuthorizeUrlµ } from './authorize.request.js'; -import { createClientStore, createTokenError } from './client.store.utils.js'; +import { createClientStore } from './client.store.effects.js'; +import { createTokenError, parseOidcArgs } from './client.store.utils.js'; import { buildTokenExchangeµ } from './exchange.request.js'; import { logoutµ } from './logout.request.js'; import { oidcApi } from './oidc.api.js'; import { sessionCheckIdTokenµ, sessionCheckNoneµ } from './session.micros.js'; import { isExpiryWithinThreshold } from './token.utils.js'; -import { wellknownApi, wellknownSelector } from './wellknown.api.js'; -import type { CustomLogger, LogLevel } from '@forgerock/sdk-logger'; -import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { ActionTypes } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; import type { GenericError, GetAuthorizationUrlOptions } from '@forgerock/sdk-types'; import type { StorageConfig } from '@forgerock/storage'; import type { AuthorizationError, AuthorizationSuccess } from './authorize.request.types.js'; +import type { RawOidcArgs } from './client.store.types.js'; import type { GetTokensOptions, LogoutErrorResult, @@ -34,7 +36,7 @@ import type { RevokeSuccessResult, UserInfoResponse, } from './client.types.js'; -import type { OauthTokens, OidcConfig } from './config.types.js'; +import type { OauthTokens } from './config.types.js'; import type { TokenExchangeErrorResponse } from './exchange.types.js'; import type { SessionCheckOptions, SessionCheckSuccess } from './session.types.js'; @@ -43,60 +45,56 @@ import type { SessionCheckOptions, SessionCheckSuccess } from './session.types.j * @description Factory function to create an OIDC client with methods for authorization, token exchange, * user info retrieval, and logout. It initializes the client with the provided configuration, * request middleware, logger, and storage options. - * @param param - configuration object containing the OIDC client configuration, request middleware, logger, - * @param {OidcConfig} param.config - OIDC configuration including server details, client ID, redirect URI, + * @param raw - configuration object containing the OIDC client configuration, request middleware, logger, + * @param {OidcConfig} raw.config - OIDC configuration including server details, client ID, redirect URI, * storage options, scope, and response type. - * @param {RequestMiddleware} param.requestMiddleware - optional array of request middleware functions to process requests. - * @param {{ level: LogLevel, custom: CustomLogger }} param.logger - optional logger configuration with log level and custom logger. - * @param {Partial} param.storage - optional storage configuration for persisting OIDC tokens. + * @param {RequestMiddleware} raw.requestMiddleware - optional array of request middleware functions to process requests. + * @param {{ level: LogLevel, custom: CustomLogger }} raw.logger - optional logger configuration with log level and custom logger. + * @param {Partial} raw.storage - optional storage configuration for persisting OIDC tokens. + * @param {unknown} raw.store - optional existing SDK store to share across clients; validated at runtime via `isSdkStoreHandle`. * @returns {ReturnType} - Returns an object with methods for authorization, token exchange, user info retrieval, and logout. */ -export async function oidc({ - config, - requestMiddleware, - logger, - storage, -}: { - config: OidcConfig; - requestMiddleware?: RequestMiddleware[]; - logger?: { - level: LogLevel; - custom?: CustomLogger; - }; - storage?: Partial; -}) { +export async function oidc( + raw: RawOidcArgs, +) { + const parsed = parseOidcArgs(raw); + if ('error' in parsed) return parsed; + + const { config, requestMiddleware, logger, storage, store: sharedStore } = parsed; + const log = loggerFn({ level: logger?.level ?? config.log ?? 'error', custom: logger?.custom, }); const oauthThreshold = config.oauthThreshold || 30 * 1000; // Default to 30 seconds + const storageClient = createStorage({ type: storage?.type || 'localStorage', name: storage?.name || config.clientId, prefix: storage?.prefix || 'pic', ...storage, } as StorageConfig); - const store = createClientStore({ requestMiddleware, logger: log }); - - if (!config?.serverConfig?.wellknown) { - return { - error: 'Requires a wellknown url initializing this factory.', - type: 'argument_error', - }; - } - if (!config?.clientId) { - return { - error: 'Requires a clientId.', - type: 'argument_error', - }; - } + /** + * Validate against the network *before* injecting anything. RTK's `inject` + * is irreversible, so mutating a caller-owned store and then failing would + * leave it permanently carrying a slice from a call that never succeeded. + * The well-known slice is mounted from the start, so discovery can be + * fetched (and cached for the eventual client) on the un-mutated store. + */ + const baseStore = sharedStore ?? createSdkStore(); const wellknownUrl = config.serverConfig.wellknown; - const { data, error } = await store.dispatch( - wellknownApi.endpoints.configuration.initiate(wellknownUrl), - ); + // The shared SdkStore type is deliberately state-agnostic, so dispatch is + // widened here; the initiated thunk and its result shape are fixed by + // wellknownApi itself. + const { data, error } = (await baseStore.store.dispatch( + wellknownApi.endpoints.configuration.initiate(wellknownUrl) as never, + )) as { data?: { require_pushed_authorization_requests?: boolean }; error?: unknown }; if (error || !data) { + // RTK Query retains rejected query entries. Clear them before returning so a + // later client sharing this store performs a fresh discovery request. + baseStore.store.dispatch(wellknownApi.util.resetApiState() as never); log.error(`Error fetching wellknown config. Please check the URL: ${wellknownUrl}`); return { error: `Failed to fetch well-known configuration from: ${wellknownUrl}`, @@ -112,9 +110,22 @@ export async function oidc({ }; } + // Validation is complete — only now does the store gain the oidc slice. + // When we created the base store ourselves, inject into *it* so the already- + // cached discovery document carries over to the returned store. + const handle = createClientStore({ + requestMiddleware, + logger: log, + store: sharedStore ?? baseStore, + clientId: config.clientId, + }); + const { store } = handle; + const useParFlow = config.par ?? data?.require_pushed_authorization_requests === true; return { + /** Pass to another SDK client's `store` option to share this store. */ + store: handle as SdkStore, // Pass store methods to the client subscribe: store.subscribe, diff --git a/packages/oidc-client/src/lib/client.store.types.test.ts b/packages/oidc-client/src/lib/client.store.types.test.ts new file mode 100644 index 00000000000..0eb80b2b9e1 --- /dev/null +++ b/packages/oidc-client/src/lib/client.store.types.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +import { logger as loggerFn } from '@forgerock/sdk-logger'; +import { describe, expect, it } from 'vitest'; + +import { createClientStore } from './client.store.effects.js'; +import { parseOidcArgs } from './client.store.utils.js'; + +import type { OidcConfig } from './config.types.js'; + +const validConfig: OidcConfig = { + clientId: 'test-client', + redirectUri: 'https://example.com/callback.html', + scope: 'openid profile', + serverConfig: { wellknown: 'https://example.com/.well-known/openid-configuration' }, + responseType: 'code', +}; + +describe('parseOidcArgs', () => { + it('returns ParsedOidcArgs (no error) when all required fields are present', () => { + const result = parseOidcArgs({ config: validConfig }); + + expect('type' in result).toBe(false); + if ('type' in result) return; + expect(result.config.clientId).toBe('test-client'); + expect(result.config.serverConfig.wellknown).toBe( + 'https://example.com/.well-known/openid-configuration', + ); + expect(result.store).toBeUndefined(); + }); + + it('returns argument_error when store is a non-SDK-store object', () => { + const result = parseOidcArgs({ + config: validConfig, + store: { notAnSdkStore: true, dispatch: () => void 0 }, + }); + + expect(result).toMatchObject({ type: 'argument_error' }); + }); + + it('returns argument_error when config.serverConfig.wellknown is missing', () => { + const result = parseOidcArgs({ + config: { ...validConfig, serverConfig: {} as OidcConfig['serverConfig'] }, + }); + + expect(result).toMatchObject({ + type: 'argument_error', + error: 'Requires a wellknown url initializing this factory.', + }); + }); + + it('returns argument_error when config.clientId is missing', () => { + const result = parseOidcArgs({ + config: { ...validConfig, clientId: '' }, + }); + + expect(result).toMatchObject({ + type: 'argument_error', + error: 'Requires a clientId.', + }); + }); + + it('returns argument_error when clientId conflicts with the existing store', () => { + const log = loggerFn({ level: 'error' }); + // Create a real store already registered with 'existing-client'. + // createClientStore returns a handle that passes isSdkStoreHandle at runtime. + const existingHandle = createClientStore({ clientId: 'existing-client', logger: log }); + + const result = parseOidcArgs({ + config: { ...validConfig, clientId: 'different-client' }, + store: existingHandle, + }); + + expect(result).toMatchObject({ type: 'argument_error' }); + if ('type' in result) { + expect(result.error).toContain('existing-client'); + expect(result.error).toContain('Use a separate store per clientId'); + } + }); +}); diff --git a/packages/oidc-client/src/lib/client.store.types.ts b/packages/oidc-client/src/lib/client.store.types.ts new file mode 100644 index 00000000000..2d95bef67d8 --- /dev/null +++ b/packages/oidc-client/src/lib/client.store.types.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import type { CustomLogger, LogLevel } from '@forgerock/sdk-logger'; +import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; +import type { StorageConfig } from '@forgerock/storage'; + +import type { OidcConfig } from './config.types.js'; + +/** + * The raw, unvalidated input type — this is what public callers pass. + * `store` is typed as `unknown` so the parser can perform a runtime + * brand-check via `isSdkStoreHandle` before narrowing to `SdkStore`. + */ +export type RawOidcArgs = { + config: OidcConfig; + requestMiddleware?: RequestMiddleware[]; + logger?: { level: LogLevel; custom?: CustomLogger }; + storage?: Partial; + /** + * An existing SDK store to attach to, so discovery caching and state are + * shared with another client. Omit to create a store for this client alone. + * Typed as `unknown` — the parser validates this at runtime via `isSdkStoreHandle`. + */ + store?: unknown; +}; + +/** + * The parsed, trusted type — all structural validation checks have passed. + * The type system records the narrowed facts: + * - `config.serverConfig.wellknown` is a non-empty string + * - `config.clientId` is a non-empty string + * - `store` is either a valid `SdkStore` handle or `undefined` + */ +export type ParsedOidcArgs = { + config: OidcConfig & { + serverConfig: { wellknown: string }; + clientId: string; + }; + requestMiddleware?: RequestMiddleware[]; + logger?: { level: LogLevel; custom?: CustomLogger }; + storage?: Partial; + store: SdkStore | undefined; +}; diff --git a/packages/oidc-client/src/lib/client.store.utils.ts b/packages/oidc-client/src/lib/client.store.utils.ts index be2f53eb7cb..2948c1015d0 100644 --- a/packages/oidc-client/src/lib/client.store.utils.ts +++ b/packages/oidc-client/src/lib/client.store.utils.ts @@ -4,53 +4,56 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import { configureStore } from '@reduxjs/toolkit'; +import { + getClientForReducerPath, + INVALID_STORE_MESSAGE, + isSdkStoreHandle, + wellknownApi, +} from '@forgerock/sdk-store'; +import { combineSlices } from '@reduxjs/toolkit'; import { oidcApi } from './oidc.api.js'; -import { wellknownApi } from './wellknown.api.js'; -import type { logger as loggerFn } from '@forgerock/sdk-logger'; -import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; +import type { ActionTypes } from '@forgerock/sdk-request-middleware'; +import type { SdkStore } from '@forgerock/sdk-store'; import type { GenericError } from '@forgerock/sdk-types'; import type { SerializedError } from '@reduxjs/toolkit'; import type { FetchBaseQueryError } from '@reduxjs/toolkit/query'; +import type { ParsedOidcArgs, RawOidcArgs } from './client.store.types.js'; + +/** + * The canonical description of the state this client contributes. + * + * The runtime store is assembled by `injectClient`, which TypeScript cannot + * follow across lazy injection. Combining the same slices here lets the state + * type be *derived* from them rather than hand-written, so it cannot drift from + * what is actually mounted. Exported so the derived state type resolves for + * consumers, and so an application can compose the reducer itself if it wants. + */ +export const rootReducer = combineSlices(oidcApi, wellknownApi); + +export type OidcRootState = ReturnType; + /** - * @function createClientStore - * @description Creates a Redux store configured with OIDC and well-known APIs. - * @param param - Configuration options for the client store. - * @param {RequestMiddleware} param.requestMiddleware - An array of request middleware functions to be applied to the store. - * @param {ReturnType} param.logger - An optional logger function for logging messages. - * @returns { ReturnType } - Returns a configured Redux store with OIDC and well-known APIs. + * Reports the clientId already occupying a store's OIDC slot, when it differs + * from the one being initialised. + * + * `oidcApi.reducerPath` is the fixed string 'oidc', so two clients on one store + * would share a single RTK Query cache slice and silently overwrite each other's + * token state. Detecting that is cheaper than namespacing per clientId, and + * failing loudly beats corrupting tokens. + * + * @returns The conflicting clientId, or `undefined` when there is no conflict. */ -export function createClientStore({ - requestMiddleware, - logger, -}: { - requestMiddleware?: RequestMiddleware[]; - logger?: ReturnType; -}) { - return configureStore({ - reducer: { - [oidcApi.reducerPath]: oidcApi.reducer, - [wellknownApi.reducerPath]: wellknownApi.reducer, - }, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware({ - thunk: { - extraArgument: { - /** - * This becomes the `api.extra` argument, and will be passed into the - * customer query wrapper for `baseQuery` - */ - requestMiddleware, - logger, - }, - }, - }) - .concat(wellknownApi.middleware) - .concat(oidcApi.middleware), - }); +export function conflictingClientId( + store: SdkStore | undefined, + clientId: string, +): string | undefined { + if (!store) return undefined; + const existing = getClientForReducerPath(store, oidcApi.reducerPath); + if (!existing) return undefined; + return existing.clientId && existing.clientId !== clientId ? existing.clientId : undefined; } /** @@ -114,3 +117,61 @@ export function createTokenError(type: 'no_tokens' | 'no_access_token' | 'no_id_ return error; } + +/** + * @function parseOidcArgs + * @description Pure, synchronous parser for OIDC factory arguments that implements + * the "parse, don't validate" pattern. Returns a narrowed + * {@link ParsedOidcArgs} on success, or a {@link GenericError} describing + * the first structural failure found. + * + * The PAR check (which requires a network round-trip) is intentionally + * excluded — it belongs in `oidc()` after the wellknown fetch. + * @param raw - The unvalidated arguments to parse. + * @returns {ParsedOidcArgs | GenericError} + */ +export function parseOidcArgs( + raw: RawOidcArgs, +): ParsedOidcArgs | GenericError { + /** + * Validate before touching the store. RTK's `inject` is irreversible, so + * mutating a caller-owned store and *then* rejecting the arguments would leave + * them permanently carrying a slice from a call that never succeeded. + */ + if (raw.store !== undefined && !isSdkStoreHandle(raw.store)) { + return { + error: INVALID_STORE_MESSAGE, + type: 'argument_error', + }; + } + if (!raw.config?.serverConfig?.wellknown) { + return { + error: 'Requires a wellknown url initializing this factory.', + type: 'argument_error', + }; + } + if (!raw.config?.clientId) { + return { + error: 'Requires a clientId.', + type: 'argument_error', + }; + } + + /** + * `oidcApi.reducerPath` is a fixed string, so a second client on the same + * store would share one cache slice and clobber the first client's tokens. + * Re-initialising the same clientId is fine and stays idempotent. + */ + const validatedStore = raw.store as SdkStore | undefined; + const conflict = conflictingClientId(validatedStore, raw.config.clientId); + if (conflict) { + return { + error: + `This store is already in use by an OIDC client with clientId '${conflict}'. ` + + 'Use a separate store per clientId.', + type: 'argument_error', + }; + } + + return raw as unknown as ParsedOidcArgs; +} diff --git a/packages/oidc-client/src/lib/client.types.ts b/packages/oidc-client/src/lib/client.types.ts index 7e091b84fc9..914ace04ab0 100644 --- a/packages/oidc-client/src/lib/client.types.ts +++ b/packages/oidc-client/src/lib/client.types.ts @@ -4,15 +4,20 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ +import type { SdkStoreHandle } from '@forgerock/sdk-store'; import type { GenericError, GetAuthorizationUrlOptions } from '@forgerock/sdk-types'; import type { StorageConfig } from '@forgerock/storage'; import type { oidc } from './client.store.js'; -import type { createClientStore } from './client.store.utils.js'; +import type { OidcRootState } from './client.store.utils.js'; export type OidcClient = Awaited>; -export type ClientStore = ReturnType; +/** + * The inner Redux store. OIDC contributes its root state through the SDK store + * handle; internal code only needs the Redux store itself. + */ +export type ClientStore = SdkStoreHandle['store']; export type RootState = ReturnType; diff --git a/packages/oidc-client/src/lib/logout.request.test.ts b/packages/oidc-client/src/lib/logout.request.test.ts index e0591ceebac..8fc2f93df63 100644 --- a/packages/oidc-client/src/lib/logout.request.test.ts +++ b/packages/oidc-client/src/lib/logout.request.test.ts @@ -12,7 +12,7 @@ import { setupServer } from 'msw/node'; import { deepStrictEqual } from 'node:assert'; import { describe, expect, it } from 'vitest'; -import { createClientStore } from './client.store.utils.js'; +import { createClientStore } from './client.store.effects.js'; import { logoutµ } from './logout.request.js'; import type { OauthTokens, OidcConfig } from './config.types.js'; @@ -86,7 +86,7 @@ const storageClient = createStorage({ }); const logger = loggerFn({ level: 'error' }); -const store = createClientStore({ logger }); +const { store } = createClientStore({ logger }); const tokens = { accessToken: '1234567890', diff --git a/packages/oidc-client/src/lib/oidc.api.ts b/packages/oidc-client/src/lib/oidc.api.ts index 7618f86285e..c3de0a35eae 100644 --- a/packages/oidc-client/src/lib/oidc.api.ts +++ b/packages/oidc-client/src/lib/oidc.api.ts @@ -5,12 +5,13 @@ * of the MIT license. See the LICENSE file for details. */ import { iFrameManager } from '@forgerock/iframe-manager'; +import { logger as loggerFn } from '@forgerock/sdk-logger'; import { initQuery } from '@forgerock/sdk-request-middleware'; +import { clientExtra } from '@forgerock/sdk-store'; import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; import { transformError } from './oidc.api.utils.js'; -import type { logger as loggerFn } from '@forgerock/sdk-logger'; import type { ActionTypes, RequestMiddleware } from '@forgerock/sdk-request-middleware'; import type { GenericError } from '@forgerock/sdk-types'; import type { @@ -29,18 +30,43 @@ import type { SessionCheckResponseType } from './session.types.js'; const IFRAME_TIMEOUT_MS = 3000; +const OIDC_REDUCER_PATH = 'oidc'; + +/** + * This client's private slot on the store's `extraArgument`. + * + * Both fields are optional because a shared store may not have had an oidc slot + * registered yet; `oidcExtra` substitutes safe defaults so an endpoint can never + * fail on a missing slot. + */ interface Extras { - requestMiddleware: RequestMiddleware[]; - logger: ReturnType; + requestMiddleware?: RequestMiddleware[]; + logger?: ReturnType; +} + +/** Creates an error-level fallback so a missing slot degrades safely, never crashes. */ +const createFallbackLogger = () => loggerFn({ level: 'error' }); + +/** + * Resolves this client's own middleware and logger. + * + * Reads only the `oidc` slot — never a store-wide value, which on a shared + * store would belong to whichever client created it. + */ +function oidcExtra(extra: unknown): Required { + return clientExtra(extra, OIDC_REDUCER_PATH, { + requestMiddleware: [], + logger: createFallbackLogger(), + }); } export const oidcApi = createApi({ - reducerPath: 'oidc', + reducerPath: OIDC_REDUCER_PATH, baseQuery: fetchBaseQuery(), endpoints: (builder) => ({ authorizeFetch: builder.mutation({ queryFn: async ({ url }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const request: FetchArgs = { url, @@ -109,7 +135,7 @@ export const oidcApi = createApi({ }), par: builder.mutation({ queryFn: async ({ endpoint, body }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const request: FetchArgs = { url: endpoint, @@ -185,7 +211,7 @@ export const oidcApi = createApi({ { url: string; responseType: SessionCheckResponseType } >({ queryFn: async ({ url, responseType }, api) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const errorParams = ['error', 'error_description']; const request: FetchArgs = { url }; @@ -269,7 +295,7 @@ export const oidcApi = createApi({ }), sessionCheckFetch: builder.mutation<{ status: 204 }, { url: string }>({ queryFn: async ({ url }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const request: FetchArgs = { url, @@ -312,7 +338,7 @@ export const oidcApi = createApi({ }), authorizeIframe: builder.mutation({ queryFn: async ({ url }, api) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const request: FetchArgs = { url, @@ -399,7 +425,7 @@ export const oidcApi = createApi({ { idToken: string; endpoint: string; signOutRedirectUri?: string } >({ queryFn: async ({ idToken, endpoint, signOutRedirectUri }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const url = new URL(endpoint); url.searchParams.append('id_token_hint', idToken); @@ -454,7 +480,7 @@ export const oidcApi = createApi({ } >({ queryFn: async ({ code, config, endpoint, verifier }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const { clientId, redirectUri } = config; const body = new URLSearchParams({ @@ -513,7 +539,7 @@ export const oidcApi = createApi({ }), revoke: builder.mutation({ queryFn: async ({ accessToken, clientId, endpoint }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const body = new URLSearchParams({ ...(clientId ? { client_id: clientId } : {}), @@ -563,7 +589,7 @@ export const oidcApi = createApi({ }), userInfo: builder.mutation({ queryFn: async ({ accessToken, endpoint }, api, _, baseQuery) => { - const { requestMiddleware, logger } = api.extra as Extras; + const { requestMiddleware, logger } = oidcExtra(api.extra); const request: FetchArgs = { url: endpoint, diff --git a/packages/oidc-client/src/lib/shared-store.test.ts b/packages/oidc-client/src/lib/shared-store.test.ts new file mode 100644 index 00000000000..37dde9e3810 --- /dev/null +++ b/packages/oidc-client/src/lib/shared-store.test.ts @@ -0,0 +1,338 @@ +// @vitest-environment node +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { createSdkStore, isSdkStoreHandle, wellknownApi } from '@forgerock/sdk-store'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { createClientStore } from './client.store.effects.js'; +import { oidc } from './client.store.js'; +import { oidcApi } from './oidc.api.js'; + +import type { OidcConfig } from './config.types.js'; + +/** + * Coverage for the three store-ownership modes. + * + * An earlier version of these tests hand-built a lookalike handle from RTK + * primitives specifically to avoid importing another client package. That made + * them blind to the only failure mode this feature really has: the producing and + * consuming sides of the handle drifting apart. Everything here goes through the + * real `createSdkStore()` and the real `oidc()` factory instead. + */ + +const TEST_WELLKNOWN_URL = + 'https://example.pingone.com/test-env/as/.well-known/openid-configuration'; + +const mockWellknownResponse = { + issuer: 'https://example.pingone.com/test-env/as', + authorization_endpoint: 'https://example.pingone.com/test-env/as/authorize', + token_endpoint: 'https://example.pingone.com/test-env/as/token', + userinfo_endpoint: 'https://example.pingone.com/test-env/as/userinfo', + jwks_uri: 'https://example.pingone.com/test-env/as/jwks', + revocation_endpoint: 'https://example.pingone.com/test-env/as/revoke', + introspection_endpoint: 'https://example.pingone.com/test-env/as/introspect', + pushed_authorization_request_endpoint: 'https://example.pingone.com/test-env/as/par', +}; + +const oidcConfig: OidcConfig = { + clientId: 'test-client-id', + redirectUri: 'http://localhost/callback', + scope: 'openid profile', + serverConfig: { wellknown: TEST_WELLKNOWN_URL }, + responseType: 'code', +}; + +function makeStorageStub() { + const store = new Map(); + return { + getItem: (key: string) => store.get(key) ?? null, + setItem: (key: string, value: string) => store.set(key, value), + removeItem: (key: string) => store.delete(key), + clear: () => store.clear(), + get length() { + return store.size; + }, + key: (i: number) => [...store.keys()][i] ?? null, + }; +} + +let wellknownFetchCount = 0; + +beforeEach(() => { + wellknownFetchCount = 0; + vi.stubGlobal('localStorage', makeStorageStub()); + vi.stubGlobal('sessionStorage', makeStorageStub()); + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const url = typeof input === 'string' ? input : (input as Request).url; + + if (url.includes('.well-known')) { + wellknownFetchCount++; + return new Response(JSON.stringify(mockWellknownResponse), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + } + + return new Response(JSON.stringify({}), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + }); +}); + +afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); + wellknownApi.util.resetApiState(); + oidcApi.util.resetApiState(); +}); + +describe('mode 1 — implicit store (default, unchanged behaviour)', () => { + it('creates its own store and returns a working client', async () => { + // Act + const client = await oidc({ config: oidcConfig }); + + // Assert + if ('error' in client) throw new Error(`Expected oidc client, got ${client.error}`); + expect(client.subscribe).toBeInstanceOf(Function); + expect(client.token).toBeDefined(); + expect(client.authorize).toBeDefined(); + }); + + it('fetches the discovery document exactly once', async () => { + // Act + await oidc({ config: oidcConfig }); + + // Assert — absolute, not "greater than zero" + expect(wellknownFetchCount).toBe(1); + }); + + it('does not share state between two independent clients', async () => { + // Act + const first = createClientStore({}); + const second = createClientStore({}); + + // Assert + expect(first.store).not.toBe(second.store); + }); + + it('refuses a conflicting clientId in the internal store attachment', () => { + // Arrange — defense in depth preserves store ownership even if this helper is reused. + const store = createClientStore({ clientId: 'existing-client' }); + + // Act & Assert — a different clientId must not silently share the cache slice. + expect(() => + createClientStore({ clientId: 'a-different-client', store: store as never }), + ).toThrow(/already in use by an OIDC client with clientId 'existing-client'/); + }); + + it('allows the same clientId to re-attach internally', () => { + // Act & Assert — re-init with the same clientId stays idempotent. + const store = createClientStore({ clientId: 'same-client' }); + expect(() => + createClientStore({ clientId: 'same-client', store: store as never }), + ).not.toThrow(); + }); +}); + +describe('mode 3 — consumer-owned store', () => { + it('accepts a store created by createSdkStore()', async () => { + // Arrange + const store = createSdkStore(); + + // Act + const client = await oidc({ config: oidcConfig, store }); + + // Assert + if ('error' in client) throw new Error(`Expected oidc client, got ${client.error}`); + expect(client.token).toBeDefined(); + }); + + it('mounts the oidc slice onto the provided store', async () => { + // Arrange + const store = createSdkStore(); + + // Act + await oidc({ config: oidcConfig, store }); + + // Assert + expect(Object.keys(store.store.getState() as object)).toContain(oidcApi.reducerPath); + }); + + it('registers only the oidc slot on the shared registry', async () => { + // Arrange + const store = createSdkStore(); + + // Act + await oidc({ config: oidcConfig, store }); + + // Assert + expect(Object.keys(store.extra.clients)).toEqual([oidcApi.reducerPath]); + }); + + it('reuses a discovery document already cached on the shared store', async () => { + // Arrange — simulate the owning client having already fetched it + const store = createSdkStore(); + await store.store.dispatch( + wellknownApi.endpoints.configuration.initiate(TEST_WELLKNOWN_URL) as never, + ); + expect(wellknownFetchCount).toBe(1); + + // Act + await oidc({ config: oidcConfig, store }); + + // Assert — still exactly one, so oidc served from cache + expect(wellknownFetchCount).toBe(1); + }); + + it('honours this client\u2019s own requestMiddleware on a shared store', async () => { + // Arrange + const store = createSdkStore(); + + // Act + await oidc({ + config: oidcConfig, + store, + requestMiddleware: [ + (_req, _action, next) => { + next(); + }, + ], + }); + + // Assert — middleware is no longer discarded on the shared path + expect(store.extra.clients[oidcApi.reducerPath]?.requestMiddleware).toHaveLength(1); + }); +}); + +describe('wellknown failure does not leak clientId registration', () => { + it('allows a different clientId to initialize after wellknown fetch fails', async () => { + // Arrange + const store = createSdkStore(); + + // First client fails due to 500 on wellknown + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const url = typeof input === 'string' ? input : (input as Request).url; + if (url.includes('.well-known')) { + wellknownFetchCount++; + return new Response(JSON.stringify({ error: 'unavailable' }), { + status: 500, + headers: { 'Content-Type': 'application/json' }, + }); + } + return new Response(JSON.stringify({}), { status: 200 }); + }); + + const first = await oidc({ + config: { ...oidcConfig, clientId: 'first-client' }, + store, + }); + + // Assert first client failed + expect(first).toHaveProperty('type', 'wellknown_error'); + + // Reset fetch to succeed for second client + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const url = typeof input === 'string' ? input : (input as Request).url; + if (url.includes('.well-known')) { + wellknownFetchCount++; + return new Response(JSON.stringify(mockWellknownResponse), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }); + } + return new Response(JSON.stringify({}), { status: 200 }); + }); + + // Act - second client with DIFFERENT clientId should succeed + const second = await oidc({ + config: { ...oidcConfig, clientId: 'second-client' }, + store, + }); + + // Assert - this is what the PR review is asking about + // If first-client left a stale registration, second-client would get: + // "This store is already in use by an OIDC client with clientId 'first-client'" + if ('error' in second) { + throw new Error( + `BUG: Second client failed with: ${second.error}. ` + + `The clientId from the failed first client was not cleaned up.`, + ); + } + expect(second.token).toBeDefined(); + }); + + it('allows a different clientId to initialize after PAR argument error', async () => { + // Arrange + const store = createSdkStore(); + + // First client fails due to PAR validation error + vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const url = typeof input === 'string' ? input : (input as Request).url; + if (url.includes('.well-known')) { + wellknownFetchCount++; + return new Response( + JSON.stringify({ + ...mockWellknownResponse, + require_pushed_authorization_requests: true, + }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ); + } + return new Response(JSON.stringify({}), { status: 200 }); + }); + + const first = await oidc({ + config: { ...oidcConfig, clientId: 'first-client', par: false }, + store, + }); + + // Assert first client failed due to PAR validation + expect(first).toHaveProperty('type', 'argument_error'); + if ('error' in first) { + expect(first.error).toContain('PAR'); + } + + // Act - second client with DIFFERENT clientId and PAR enabled should succeed + const second = await oidc({ + config: { ...oidcConfig, clientId: 'second-client', par: true }, + store, + }); + + // Assert - PAR argument_error should also clean up client registration + if ('error' in second) { + throw new Error( + `BUG: Second client failed with: ${second.error}. ` + + `The clientId from the PAR-argument_error first client was not cleaned up.`, + ); + } + expect(second.token).toBeDefined(); + }); +}); + +describe('store handle contract', () => { + it('the handle produced for a client satisfies the shared guard', () => { + // Arrange / Act + const handle = createClientStore({}); + + // Assert — the producing side of the contract + expect(isSdkStoreHandle(handle)).toBe(true); + }); + + it('rejects a value that is not a store handle', async () => { + // Act + const client = await oidc({ + config: oidcConfig, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + store: { not: 'a store' } as any, + }); + + // Assert — a clear argument error, not a TypeError from inside RTK + expect(client).toHaveProperty('type', 'argument_error'); + expect((client as { error: string }).error).toMatch(/not a valid SDK store/i); + }); +}); diff --git a/packages/oidc-client/src/lib/store-lifecycle.test.ts b/packages/oidc-client/src/lib/store-lifecycle.test.ts new file mode 100644 index 00000000000..4990596e5cf --- /dev/null +++ b/packages/oidc-client/src/lib/store-lifecycle.test.ts @@ -0,0 +1,203 @@ +// @vitest-environment node +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { createSdkStore, wellknownApi } from '@forgerock/sdk-store'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { oidc } from './client.store.js'; +import { oidcApi } from './oidc.api.js'; + +import type { OidcConfig } from './config.types.js'; + +/** + * Lifecycle guarantees for a store the caller owns. + * + * RTK's `inject` is irreversible, so mutating a caller's store before the + * arguments have been validated leaves it permanently carrying a slice from a + * call that never succeeded. And because `oidcApi.reducerPath` is the fixed + * string 'oidc', two clients with different clientIds on one store would share + * a single cache slice and silently clobber each other's tokens. + */ + +const TEST_WELLKNOWN_URL = + 'https://example.pingone.com/test-env/as/.well-known/openid-configuration'; + +const mockWellknownResponse = { + issuer: 'https://example.pingone.com/test-env/as', + authorization_endpoint: 'https://example.pingone.com/test-env/as/authorize', + token_endpoint: 'https://example.pingone.com/test-env/as/token', + userinfo_endpoint: 'https://example.pingone.com/test-env/as/userinfo', + jwks_uri: 'https://example.pingone.com/test-env/as/jwks', + revocation_endpoint: 'https://example.pingone.com/test-env/as/revoke', +}; + +const oidcConfig: OidcConfig = { + clientId: 'test-client-id', + redirectUri: 'http://localhost/callback', + scope: 'openid profile', + serverConfig: { wellknown: TEST_WELLKNOWN_URL }, + responseType: 'code', +}; + +function makeStorageStub() { + const store = new Map(); + return { + getItem: (key: string) => store.get(key) ?? null, + setItem: (key: string, value: string) => store.set(key, value), + removeItem: (key: string) => store.delete(key), + clear: () => store.clear(), + get length() { + return store.size; + }, + key: (i: number) => [...store.keys()][i] ?? null, + }; +} + +beforeEach(() => { + vi.stubGlobal('localStorage', makeStorageStub()); + vi.stubGlobal('sessionStorage', makeStorageStub()); + vi.spyOn(globalThis, 'fetch').mockImplementation( + async () => + new Response(JSON.stringify(mockWellknownResponse), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }), + ); +}); + +afterEach(() => { + vi.unstubAllGlobals(); + vi.restoreAllMocks(); + wellknownApi.util.resetApiState(); + oidcApi.util.resetApiState(); +}); + +describe('a failed oidc() leaves a caller-owned store untouched', () => { + it('does not mount the oidc slice when wellknown is missing', async () => { + // Arrange + const store = createSdkStore(); + + // Act + const result = await oidc({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + config: { ...oidcConfig, serverConfig: {} } as any, + store, + }); + + // Assert + expect(result).toHaveProperty('type', 'argument_error'); + expect(Object.keys(store.store.getState() as object)).not.toContain(oidcApi.reducerPath); + }); + + it('does not register a client slot when clientId is missing', async () => { + // Arrange + const store = createSdkStore(); + + // Act + const result = await oidc({ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + config: { ...oidcConfig, clientId: '' } as any, + store, + }); + + // Assert + expect(result).toHaveProperty('type', 'argument_error'); + expect(store.extra.clients).toEqual({}); + }); + + it('leaves the store reusable for a subsequent valid call', async () => { + // Arrange + const store = createSdkStore(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await oidc({ config: { ...oidcConfig, clientId: '' } as any, store }); + + // Act — the caller fixes their config and retries on the same store + const client = await oidc({ config: oidcConfig, store }); + + // Assert + if ('error' in client) throw new Error(`Expected oidc client, got ${client.error}`); + expect(Object.keys(store.extra.clients)).toEqual([oidcApi.reducerPath]); + }); + + it('does not mount the oidc slice when wellknown fetch fails', async () => { + // Arrange — a store whose discovery request fails + const store = createSdkStore(); + vi.spyOn(globalThis, 'fetch').mockImplementation( + async () => new Response(JSON.stringify({ error: 'unavailable' }), { status: 500 }), + ); + + // Act + const result = await oidc({ config: oidcConfig, store }); + + // Assert — validation happens before injection, so nothing was mounted + expect(result).toHaveProperty('type', 'wellknown_error'); + expect(Object.keys(store.store.getState() as object)).not.toContain(oidcApi.reducerPath); + }); + + it('does not mount the oidc slice when PAR is required but disabled', async () => { + // Arrange — discovery succeeds but the server demands PAR + const store = createSdkStore(); + vi.spyOn(globalThis, 'fetch').mockImplementation( + async () => + new Response( + JSON.stringify({ ...mockWellknownResponse, require_pushed_authorization_requests: true }), + { status: 200, headers: { 'Content-Type': 'application/json' } }, + ), + ); + + // Act + const result = await oidc({ config: { ...oidcConfig, par: false }, store }); + + // Assert + expect(result).toHaveProperty('type', 'argument_error'); + expect(Object.keys(store.store.getState() as object)).not.toContain(oidcApi.reducerPath); + }); +}); + +describe('one oidc client per shared store', () => { + it('is idempotent when re-initialised with the same clientId', async () => { + // Arrange + const store = createSdkStore(); + await oidc({ config: oidcConfig, store }); + + // Act + const second = await oidc({ config: oidcConfig, store }); + + // Assert — re-init is a legitimate operation + if ('error' in second) throw new Error(`Expected oidc client, got ${second.error}`); + expect(second.token).toBeDefined(); + }); + + it('errors when a second client with a different clientId joins the same store', async () => { + // Arrange + const store = createSdkStore(); + await oidc({ config: oidcConfig, store }); + + // Act + const second = await oidc({ + config: { ...oidcConfig, clientId: 'a-different-client' }, + store, + }); + + // Assert — silently sharing one 'oidc' cache slice would clobber tokens + expect(second).toHaveProperty('type', 'argument_error'); + expect((second as { error: string }).error).toMatch(/clientId/i); + }); + + it('allows different clientIds on separate stores', async () => { + // Act + const first = await oidc({ config: oidcConfig, store: createSdkStore() }); + const second = await oidc({ + config: { ...oidcConfig, clientId: 'a-different-client' }, + store: createSdkStore(), + }); + + // Assert + expect('error' in first).toBe(false); + expect('error' in second).toBe(false); + }); +}); diff --git a/packages/oidc-client/src/types.ts b/packages/oidc-client/src/types.ts index 9496a4ae6c4..3913fc829ad 100644 --- a/packages/oidc-client/src/types.ts +++ b/packages/oidc-client/src/types.ts @@ -24,4 +24,10 @@ export type { CustomStorageObject } from '@forgerock/sdk-types'; // Re-export functions needed to resolve OidcClient and ClientStore type aliases export { oidc } from './lib/client.store.js'; -export { createClientStore } from './lib/client.store.utils.js'; +// RawOidcArgs is a parameter type of oidc() and must be re-exported so consumers can type call-sites +export type { RawOidcArgs } from './lib/client.store.types.js'; +export type { OidcRootState } from './lib/client.store.utils.js'; +export { rootReducer } from './lib/client.store.utils.js'; + +import type { oidc } from './lib/client.store.js'; +export type OidcClient = Awaited>; diff --git a/packages/oidc-client/tsconfig.lib.json b/packages/oidc-client/tsconfig.lib.json index d689866941f..7335f658954 100644 --- a/packages/oidc-client/tsconfig.lib.json +++ b/packages/oidc-client/tsconfig.lib.json @@ -39,6 +39,9 @@ }, { "path": "../sdk-effects/iframe-manager/tsconfig.lib.json" + }, + { + "path": "../sdk-effects/store/tsconfig.lib.json" } ], "exclude": [ diff --git a/packages/sdk-effects/oidc/src/index.ts b/packages/sdk-effects/oidc/src/index.ts index 6ed59d5aa9a..48e3fb9726e 100644 --- a/packages/sdk-effects/oidc/src/index.ts +++ b/packages/sdk-effects/oidc/src/index.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. * * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. @@ -8,4 +8,3 @@ export * from './lib/authorize.effects.js'; export * from './lib/authorize.utils.js'; export * from './lib/state-pkce.effects.js'; -export * from './lib/wellknown.effects.js'; diff --git a/packages/sdk-effects/store/README.md b/packages/sdk-effects/store/README.md new file mode 100644 index 00000000000..0a872aaf9ac --- /dev/null +++ b/packages/sdk-effects/store/README.md @@ -0,0 +1,306 @@ +# @forgerock/sdk-store + +Shared Redux store infrastructure and OpenID Connect discovery API for the Ping JavaScript SDK. + +This is an internal building block. You do not normally install it directly — `@forgerock/oidc-client`, `@forgerock/davinci-client`, and `@forgerock/journey-client` depend on it. Install it explicitly only if your application wants to own the SDK store itself. + +## Table of Contents + +- [Why this package exists](#why-this-package-exists) +- [Installation](#installation) +- [Sharing a store between clients](#sharing-a-store-between-clients) +- [Well-known discovery](#well-known-discovery) +- [API Reference](#api-reference) +- [Building](#building) +- [Testing](#testing) + +## Why this package exists + +Every SDK client needs the authorization server's OpenID Connect discovery document. Before this package, each client package defined its own `wellknownApi` instance. Three separate RTK Query APIs meant three separate caches, so an application using more than one client fetched the same `.well-known/openid-configuration` document once per client. + +This package holds the **single canonical `wellknownApi` instance**. When SDK clients share a Redux store, they share one cache entry per URL, and the discovery document is fetched exactly once. + +## Installation + +```bash +pnpm add @forgerock/sdk-store +``` + +## Sharing a store between clients + +An application using more than one SDK client can have them share a single Redux store, so the discovery document is fetched once and state lives in one place. There are three ways to arrange this, and the default requires no changes at all. + +### Mode 1 — implicit (default) + +Each client creates its own store. Nothing is shared. This is the behaviour you get by omitting `store`, and it is what every existing application already does. + +```typescript +const client = await oidc({ config }); +``` + +### Mode 2 — one client owns the store + +`davinci()` and `journey()` expose the store they created as `client.store`. Pass it to another client to attach to it. This is the common case. + +```typescript +const davinciClient = await davinci({ config: davinciConfig }); +const oidcClient = await oidc({ config: oidcConfig, store: davinciClient.store }); +// The discovery document was fetched by davinci(); oidc() reads it from cache. +``` + +### Mode 3 — the application owns the store + +Call `createSdkStore()` yourself when you want to control the store's lifetime, or to attach clients in an order that has no natural owner. + +```typescript +import { createSdkStore } from '@forgerock/sdk-store'; + +const store = createSdkStore(); +const davinciClient = await davinci({ config: davinciConfig, store }); +const oidcClient = await oidc({ config: oidcConfig, store }); +``` + +### What is shared, and what is not + +Sharing a store shares **cached data**, not **configuration**. This distinction matters: + +| Shared across clients | Private to each client | +| -------------------------------------------------- | ---------------------- | +| The well-known discovery cache (one fetch per URL) | `requestMiddleware` | +| The Redux state tree and its subscribers | `logger` | + +Each client's `requestMiddleware` and `logger` are registered under its own key on the store and are resolved only by that client's own requests. A middleware you pass to `davinci()` will never run against an OIDC token exchange, and vice versa. Pass them to the client whose requests they are meant to affect: + +```typescript +const store = createSdkStore(); + +// Applies only to DaVinci flow requests. +await davinci({ config: davinciConfig, store, requestMiddleware: [davinciMiddleware] }); + +// Applies only to OIDC requests (authorize, token, revoke, userinfo, end session). +await oidc({ config: oidcConfig, store, requestMiddleware: [oidcMiddleware] }); +``` + +### Limitations + +- **One OIDC client per store.** `oidcApi` mounts at a fixed key, so two OIDC clients on one store would share a single cache slice and overwrite each other's token state. Initialising a second client with a _different_ `clientId` returns an `argument_error`; re-initialising the _same_ `clientId` is allowed and idempotent. Use a separate store per `clientId`. +- **Attaching is permanent.** A client cannot be detached from a store. Discard the store instead. + +## Well-known discovery + +`wellknownApi` is a standard RTK Query API. Mount its reducer and middleware, then dispatch the `configuration` endpoint with the discovery URL as the cache key. + +```typescript +import { configureStore } from '@reduxjs/toolkit'; +import { wellknownApi, wellknownSelector } from '@forgerock/sdk-store'; + +const store = configureStore({ + reducer: { [wellknownApi.reducerPath]: wellknownApi.reducer }, + middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(wellknownApi.middleware), +}); + +const url = 'https://auth.example.com/as/.well-known/openid-configuration'; + +// Fetches on first call; served from cache thereafter. +const { data, error } = await store.dispatch(wellknownApi.endpoints.configuration.initiate(url)); + +// Or read the cached value out of state at any time. +const wellknown = wellknownSelector(url, store.getState()); +``` + +The endpoint never throws. A network failure, a non-2xx response, or a payload missing the required `issuer` / `authorization_endpoint` / `token_endpoint` fields all resolve to `{ error }`. + +## API Reference + +### `createSdkStore()` + +```typescript +function createSdkStore(): SdkStore; +``` + +Creates a Redux store that SDK clients can share. The well-known discovery slice is mounted up front; all other slices are injected lazily via `injectClient()`. Applications may call this directly when they want to own the store's lifetime; otherwise each client factory creates one on demand. + +### `injectClient(handle, options)` + +```typescript +function injectClient>( + handle: SdkStore, + options: InjectClientOptions, +): SdkStoreHandle; +``` + +Attaches a client to an existing store: mounts its RTK Query API reducer and middleware, injects any additional slices, and registers the client's private configuration slot. Safe to call multiple times for the same client — RTK deduplicates reducer injection. Throws `Error` (using `INVALID_STORE_MESSAGE`) if `handle` is not a valid SDK store handle. + +### `isSdkStoreHandle(value)` + +```typescript +function isSdkStoreHandle(value: unknown): value is SdkStore; +``` + +Type guard. Returns `true` when `value` structurally matches a valid SDK store handle (i.e. it was produced by `createSdkStore()` or returned by a client factory). Used by client factories to validate caller-supplied `store` arguments before touching the store. + +### `INVALID_STORE_MESSAGE` + +```typescript +const INVALID_STORE_MESSAGE: string; +``` + +Human-readable error message emitted whenever an argument fails the `isSdkStoreHandle()` check. Exported so every client package references the same string rather than duplicating it. + +### `clientExtra(extra, reducerPath, defaults?)` + +```typescript +function clientExtra( + extra: unknown, + reducerPath: string, + defaults?: Partial, +): Slot; +``` + +Resolves the calling client's own per-client slot from a store's thunk `extraArgument`. Runs on every request and never throws — an unrecognised `extra` yields `defaults` (or `{}`) instead of an error. When `defaults` are supplied, any slot key that is absent or `undefined` is filled from `defaults`. + +### `initWellknownQuery(url)` + +```typescript +function initWellknownQuery(url: string): { + applyQuery(callback: QueryCallback): Promise>; +}; +``` + +Creates a well-known query builder for use inside an RTK Query `queryFn`. Constructs the fetch request (with `Accept: application/json`), executes it through the provided `callback`, and validates the response via `isValidWellknownResponse`. Network failures and invalid responses are both normalised to a structured `WellknownQueryError`. + +```typescript +queryFn: async (url, _api, _extra, baseQuery) => { + return initWellknownQuery(url).applyQuery(async (req) => await baseQuery(req)); +}; +``` + +### `isValidWellknownResponse(data)` + +```typescript +function isValidWellknownResponse(data: unknown): data is WellknownResponse; +``` + +Returns `true` when `data` contains the minimum required OIDC well-known fields: `issuer`, `authorization_endpoint`, and `token_endpoint` (all strings). + +### Types + +#### `SdkStore` + +```typescript +interface SdkStore { + readonly store: { + getState: () => unknown; + dispatch: (action: never) => unknown; + subscribe: (listener: () => void) => () => void; + }; + readonly rootReducer: InjectableRootReducer; + readonly dynamicMiddleware: DynamicMiddleware; + readonly extra: SdkStoreRegistry; +} +``` + +State-agnostic shared Redux store contract. This is the type that travels between client packages: `davinci()` and `journey()` expose one as `client.store`; `oidc()` and `journey()` accept one as the `store` option. + +#### `SdkStoreHandle` + +```typescript +interface SdkStoreHandle> extends SdkStore { + readonly store: Store & { + dispatch: ThunkDispatch; + }; + readonly rootReducer: InjectableRootReducer & Reducer; +} +``` + +Store handle with a known state shape `S`. Returned by each client's own store factory so internal code gets full typing on `getState()` and `dispatch()`. Assignable to `SdkStore` for hand-off to another client. + +#### `SdkStoreRegistry` + +```typescript +interface SdkStoreRegistry { + readonly clients: Record; +} +``` + +The mutable client registry carried as the store's `extraArgument`. `configureStore` captures this by reference, so slots added after store creation are visible to every subsequent request. + +#### `ClientSlot` + +```typescript +interface ClientSlot { + readonly requestMiddleware?: readonly unknown[]; + readonly logger?: unknown; + readonly clientId?: string; +} +``` + +Per-client slot on a store's thunk `extraArgument`. Intentionally loose so `sdk-store` does not depend on `sdk-request-middleware` or `sdk-logger` types. Each client narrows this to its own concrete shape at the point of use. + +#### `InjectClientOptions` + +```typescript +interface InjectClientOptions { + readonly api: { reducerPath: string; reducer: Reducer; middleware: Middleware }; + readonly reducerPath: string; + readonly slices?: readonly { name: string; reducer: Reducer }[]; + readonly requestMiddleware?: readonly unknown[]; + readonly logger?: unknown; + readonly clientId?: string; +} +``` + +Options passed to `injectClient()` describing the client attaching itself to a store. `api` provides the RTK Query API (its reducer and middleware are both mounted). `slices` lists any additional Redux slices the client owns. `reducerPath` is used as the registry key — normally matches `api.reducerPath`. + +### `wellknownApi` + +The canonical RTK Query API instance. `reducerPath` is `'wellknown'`. + +| Member | Description | +| -------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `wellknownApi.reducer` | Mount at `wellknownApi.reducerPath` | +| `wellknownApi.middleware` | Add to the store middleware chain | +| `wellknownApi.endpoints.configuration` | `builder.query` — the argument is the discovery URL and doubles as the cache key | + +### `createWellknownSelector(wellknownUrl)` + +Returns a memoized selector for the cached discovery document, or `undefined` if it has not been fetched. + +Repeated calls with the same URL return the **same selector instance**, so memoization is shared across call sites rather than being rebuilt (and therefore always cold) on each call. + +```typescript +const selectWellknown = createWellknownSelector(url); +const wellknown = selectWellknown(store.getState()); + +createWellknownSelector(url) === createWellknownSelector(url); // true +``` + +### `wellknownSelector(wellknownUrl, state)` + +Convenience wrapper that resolves the selector and immediately applies it to `state`. Use this for one-off reads; use `createWellknownSelector` when you want to hold the selector. + +```typescript +const wellknown = wellknownSelector(url, store.getState()); +``` + +### `WellknownState` + +The minimum state shape required by the selectors. Useful for constraining generic state types: + +```typescript +function readIssuer(url: string, state: S) { + return wellknownSelector(url, state)?.issuer; +} +``` + +## Building + +```bash +pnpm nx build @forgerock/sdk-store +``` + +## Testing + +```bash +pnpm nx test @forgerock/sdk-store +``` diff --git a/packages/sdk-effects/store/eslint.config.mjs b/packages/sdk-effects/store/eslint.config.mjs new file mode 100644 index 00000000000..550383b33f6 --- /dev/null +++ b/packages/sdk-effects/store/eslint.config.mjs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import baseConfig from '../../../eslint.config.mjs'; + +export default [ + ...baseConfig, + { + files: ['**/*.json'], + rules: { + '@nx/dependency-checks': [ + 'warn', + { + ignoredFiles: [ + '{projectRoot}/eslint.config.{js,cjs,mjs}', + '{projectRoot}/vite.config.{js,ts,mjs,mts}', + ], + }, + ], + }, + languageOptions: { + parser: (await import('jsonc-eslint-parser')).default, + }, + }, +]; diff --git a/packages/sdk-effects/store/package.json b/packages/sdk-effects/store/package.json new file mode 100644 index 00000000000..9a2eb694520 --- /dev/null +++ b/packages/sdk-effects/store/package.json @@ -0,0 +1,40 @@ +{ + "name": "@forgerock/sdk-store", + "version": "0.0.0", + "private": false, + "repository": { + "type": "git", + "url": "git+https://github.com/ForgeRock/ping-javascript-sdk.git", + "directory": "packages/sdk-effects/store" + }, + "description": "Shared Redux store and OpenID Connect discovery API for the Ping JavaScript SDK", + "license": "MIT", + "author": "ForgeRock", + "sideEffects": false, + "type": "module", + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./dist/src/index.js", + "default": "./dist/src/index.js" + }, + "./package.json": "./package.json" + }, + "main": "./dist/src/index.js", + "module": "./dist/src/index.js", + "types": "./dist/src/index.d.ts", + "files": ["dist"], + "scripts": { + "build": "pnpm nx nxBuild", + "lint": "pnpm nx nxLint", + "test": "pnpm nx nxTest", + "test:watch": "pnpm nx nxTest --watch" + }, + "dependencies": { + "@forgerock/sdk-types": "workspace:*", + "@reduxjs/toolkit": "catalog:" + }, + "nx": { + "tags": ["scope:sdk-effects"] + } +} diff --git a/packages/sdk-effects/store/src/index.ts b/packages/sdk-effects/store/src/index.ts new file mode 100644 index 00000000000..3dd24aa4ab5 --- /dev/null +++ b/packages/sdk-effects/store/src/index.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +export { wellknownApi, wellknownSelector, createWellknownSelector } from './lib/wellknown.api.js'; +export type { WellknownState } from './lib/wellknown.api.js'; + +export { initWellknownQuery, isValidWellknownResponse } from './lib/wellknown.effects.js'; + +export { + assertValidStore, + clientExtra, + getClientForReducerPath, + INVALID_STORE_MESSAGE, + isSdkStoreHandle, +} from './lib/store.utils.js'; + +export { injectClient } from './lib/store.micros.js'; + +export { createSdkStore, unregisterClient } from './lib/store.effects.js'; +export type { + ClientSlot, + InjectClientOptions, + SdkStore, + SdkStoreHandle, + SdkStoreRegistry, +} from './lib/store.types.js'; diff --git a/packages/sdk-effects/store/src/lib/store.effects.test.ts b/packages/sdk-effects/store/src/lib/store.effects.test.ts new file mode 100644 index 00000000000..9e16b6492a6 --- /dev/null +++ b/packages/sdk-effects/store/src/lib/store.effects.test.ts @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { createSlice } from '@reduxjs/toolkit'; +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; +import { describe, expect, it, vi } from 'vitest'; + +import { createSdkStore, unregisterClient } from './store.effects.js'; +import { injectClient } from './store.micros.js'; +import { isSdkStoreHandle } from './store.utils.js'; +import { wellknownApi } from './wellknown.api.js'; + +const fakeApi = createApi({ + reducerPath: 'fake', + baseQuery: fetchBaseQuery(), + endpoints: (builder) => ({ + ping: builder.query({ queryFn: async () => ({ data: {} }) }), + }), +}); + +const otherApi = createApi({ + reducerPath: 'other', + baseQuery: fetchBaseQuery(), + endpoints: (builder) => ({ + ping: builder.query({ queryFn: async () => ({ data: {} }) }), + }), +}); + +const fakeSlice = createSlice({ + name: 'fakeSlice', + initialState: { value: 0 }, + reducers: { bump: (state) => ({ value: state.value + 1 }) }, +}); + +const davinciConfigSlice = createSlice({ + name: 'config', + reducerPath: 'davinciConfig', + initialState: { clientId: '' }, + reducers: { + set: (_state, action: { payload: string }) => ({ clientId: action.payload }), + }, +}); + +const journeyConfigSlice = createSlice({ + name: 'journeyConfig', + reducerPath: 'journeyConfig', + initialState: { clientId: '' }, + reducers: { + set: (_state, action: { payload: string }) => ({ clientId: action.payload }), + }, +}); + +/** Stand-in for a request middleware; sdk-store does not depend on its type. */ +function noopMiddleware() { + return (_req: unknown, _action: unknown, next: () => unknown) => { + next(); + }; +} + +describe('createSdkStore', () => { + it('produces a handle that satisfies the shared contract', () => { + // Act + const handle = createSdkStore(); + + // Assert + expect(isSdkStoreHandle(handle)).toBe(true); + }); + + it('mounts the wellknown slice so discovery is shared from the start', () => { + // Act + const handle = createSdkStore(); + + // Assert + expect(Object.keys(handle.store.getState() as object)).toContain(wellknownApi.reducerPath); + }); + + it('starts with an empty client registry on the store extra', () => { + // Act + const handle = createSdkStore(); + + // Assert + expect(handle.extra.clients).toEqual({}); + }); + + it('returns an independent store on each call', () => { + // Act + const a = createSdkStore(); + const b = createSdkStore(); + + // Assert + expect(a.store).not.toBe(b.store); + expect(a.extra).not.toBe(b.extra); + }); +}); + +describe('isSdkStoreHandle', () => { + it.each([ + ['undefined', undefined], + ['null', null], + ['a number', 42], + ['a string', 'store'], + ['an empty object', {}], + [ + 'an object missing dynamicMiddleware', + { store: {}, rootReducer: { inject: () => undefined } }, + ], + ['a bare redux store', { dispatch: () => undefined, getState: () => ({}) }], + [ + 'a handle whose clients registry is null', + { + store: { + dispatch: () => undefined, + getState: () => ({}), + subscribe: () => () => undefined, + }, + rootReducer: { inject: () => undefined }, + dynamicMiddleware: { addMiddleware: () => undefined }, + extra: { clients: null }, + }, + ], + [ + 'a store without subscribe', + { + store: { dispatch: () => undefined, getState: () => ({}) }, + rootReducer: { inject: () => undefined }, + dynamicMiddleware: { addMiddleware: () => undefined }, + extra: { clients: {} }, + }, + ], + ] as [string, unknown][])('rejects %s', (_label, candidate) => { + // Assert — a bad handle must be detectable before we mutate anything + expect(isSdkStoreHandle(candidate)).toBe(false); + }); + + it('accepts a real handle', () => { + expect(isSdkStoreHandle(createSdkStore())).toBe(true); + }); +}); + +describe('injectClient', () => { + it('mounts the client api reducer and makes it readable immediately', () => { + // Arrange + const handle = createSdkStore(); + + // Act + injectClient(handle, { api: fakeApi }); + + // Assert — no manual dispatch required by the caller + expect(Object.keys(handle.store.getState() as object)).toContain('fake'); + }); + + it('mounts additional slices supplied by the client', () => { + // Arrange + const handle = createSdkStore(); + + // Act + injectClient(handle, { + api: fakeApi, + slices: [fakeSlice], + }); + + // Assert + expect(Object.keys(handle.store.getState() as object)).toContain('fakeSlice'); + }); + + it('isolates same-action config slices in a shared store', () => { + // Arrange — production client config slices each expose a `set` action. + const handle = createSdkStore(); + + const davinciStore = injectClient<{ davinciConfig: { clientId: string } }>(handle, { + api: fakeApi, + slices: [davinciConfigSlice], + }); + const sharedStore = injectClient<{ + davinciConfig: { clientId: string }; + journeyConfig: { clientId: string }; + }>(davinciStore, { + api: otherApi, + slices: [journeyConfigSlice], + }); + + // Act + sharedStore.store.dispatch(davinciConfigSlice.actions.set('davinci-client')); + + // Assert — reducer paths mount state; unique slice names namespace actions. + expect(davinciConfigSlice.actions.set('x').type).toBe('config/set'); + expect(journeyConfigSlice.actions.set('x').type).toBe('journeyConfig/set'); + expect(sharedStore.store.getState()).toMatchObject({ + davinciConfig: { clientId: 'davinci-client' }, + journeyConfig: { clientId: '' }, + }); + + sharedStore.store.dispatch(journeyConfigSlice.actions.set('journey-client')); + + expect(sharedStore.store.getState()).toMatchObject({ + davinciConfig: { clientId: 'davinci-client' }, + journeyConfig: { clientId: 'journey-client' }, + }); + }); + + it('registers the client slot under its api reducerPath', () => { + // Arrange + const handle = createSdkStore(); + const mw = noopMiddleware(); + + // Act + injectClient(handle, { + api: fakeApi, + requestMiddleware: [mw], + }); + + // Assert + expect(handle.extra.clients['fake']?.requestMiddleware).toEqual([mw]); + }); + + it('keeps each client slot separate', () => { + // Arrange + const handle = createSdkStore(); + const first = noopMiddleware(); + const second = noopMiddleware(); + + // Act + injectClient(handle, { + api: fakeApi, + requestMiddleware: [first], + }); + injectClient(handle, { + api: otherApi, + requestMiddleware: [second], + }); + + // Assert + expect(handle.extra.clients['fake']?.requestMiddleware).toEqual([first]); + expect(handle.extra.clients['other']?.requestMiddleware).toEqual([second]); + }); + + it('adds the api middleware to the dynamic chain', () => { + // Arrange + const handle = createSdkStore(); + const spy = vi.spyOn(handle.dynamicMiddleware, 'addMiddleware'); + + // Act + injectClient(handle, { api: fakeApi }); + + // Assert + expect(spy).toHaveBeenCalledWith(fakeApi.middleware); + }); + + it('is idempotent for repeated injection of the same client', () => { + // Arrange + const handle = createSdkStore(); + const spy = vi.spyOn(handle.dynamicMiddleware, 'addMiddleware'); + + // Act + injectClient(handle, { api: fakeApi }); + const afterFirst = Object.keys(handle.store.getState() as object).sort(); + injectClient(handle, { api: fakeApi }); + + // Assert + expect(Object.keys(handle.store.getState() as object).sort()).toEqual(afterFirst); + expect(spy).toHaveBeenCalledTimes(1); // middleware must not be double-registered + }); + + it('throws a descriptive error when handed something that is not a handle', () => { + // Assert — better than a TypeError from deep inside a factory + expect(() => + // eslint-disable-next-line @typescript-eslint/no-explicit-any + injectClient({} as any, { api: fakeApi }), + ).toThrow(/not a valid SDK store/i); + }); +}); + +describe('unregisterClient', () => { + it('removes the client slot from the registry', () => { + // Arrange + const handle = createSdkStore(); + injectClient(handle, { + api: fakeApi, + clientId: 'test-client', + }); + expect(handle.extra.clients['fake']).toBeDefined(); + + // Act + unregisterClient(handle, fakeApi.reducerPath); + + // Assert + expect(handle.extra.clients['fake']).toBeUndefined(); + }); + + it('allows a different clientId to be injected after unregister', () => { + // Arrange + const handle = createSdkStore(); + injectClient(handle, { + api: fakeApi, + clientId: 'first-client', + }); + + // Act + unregisterClient(handle, fakeApi.reducerPath); + injectClient(handle, { + api: fakeApi, + clientId: 'second-client', + }); + + // Assert + expect(handle.extra.clients['fake']?.clientId).toBe('second-client'); + }); + + it('is idempotent — no error when slot does not exist', () => { + // Arrange + const handle = createSdkStore(); + // No injection + + // Act & Assert — should not throw + expect(() => unregisterClient(handle, 'nonexistent')).not.toThrow(); + expect(handle.extra.clients['nonexistent']).toBeUndefined(); + }); +}); diff --git a/packages/sdk-effects/store/src/lib/store.effects.ts b/packages/sdk-effects/store/src/lib/store.effects.ts new file mode 100644 index 00000000000..fc0f0b558c1 --- /dev/null +++ b/packages/sdk-effects/store/src/lib/store.effects.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { combineSlices, configureStore, createDynamicMiddleware } from '@reduxjs/toolkit'; + +import { wellknownApi } from './wellknown.api.js'; + +import type { SdkStore, SdkStoreRegistry } from './store.types.js'; + +/** + * Creates a Redux store that SDK clients can share. + * + * Only the well-known discovery slice is mounted up front — that is the one + * piece every client needs, and mounting it once is what makes the discovery + * document fetch exactly once per URL no matter how many clients attach. + * Everything else arrives through {@link injectClient}. + * + * Applications may call this directly to own the store themselves, but they do + * not have to: each client factory creates one on demand when none is passed. + */ +export function createSdkStore(): SdkStore { + const dynamicMiddleware = createDynamicMiddleware(); + const rootReducer = combineSlices(wellknownApi).withLazyLoadedSlices(); + + // Captured by reference so slots registered later are visible to every request. + const extra: SdkStoreRegistry = { clients: {} }; + + const store = configureStore({ + reducer: rootReducer, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ thunk: { extraArgument: extra } }) + .concat(wellknownApi.middleware) + .concat(dynamicMiddleware.middleware), + }); + + return { + store, + rootReducer, + dynamicMiddleware, + extra, + } as unknown as SdkStore; +} + +/** + * Removes a client slot from the registry when initialization fails. + * + * Injection is irreversible at the RTK level, but clearing the registry slot + * lets a different client retry with a different clientId. Without this, a + * failed wellknown fetch would permanently "own" the store for the first + * clientId, preventing any other client from attaching. + * + * @param store - The store to clean up. + * @param reducerPath - The client's reducer path (e.g., 'oidc'). + */ +export function unregisterClient(store: SdkStore, reducerPath: string): void { + // The registry is mutable by design — this is the cleanup counterpart to injectClient. + delete (store.extra.clients as Record)[reducerPath]; +} diff --git a/packages/sdk-effects/store/src/lib/store.micros.ts b/packages/sdk-effects/store/src/lib/store.micros.ts new file mode 100644 index 00000000000..0444912d0cd --- /dev/null +++ b/packages/sdk-effects/store/src/lib/store.micros.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { INVALID_STORE_MESSAGE, isSdkStoreHandle } from './store.utils.js'; + +import type { InjectClientOptions, SdkStore, SdkStoreHandle } from './store.types.js'; + +/** Forces `combineSlices` to recompute state after lazy reducer injection. */ +const RECOMPUTE_ACTION = { type: '@@sdk-store/recompute' } as const; + +/** + * Attaches a client to a store: mounts its reducers and middleware, and + * registers its private slot on the store's client registry. + * + * Safe to call more than once for the same client. Client slices must each have + * a distinct reducer path when sharing a store. + * + * @throws If `handle` is not a valid SDK store handle. + */ +export function injectClient>( + handle: SdkStore, + options: InjectClientOptions, +): SdkStoreHandle { + if (!isSdkStoreHandle(handle)) { + throw new Error(INVALID_STORE_MESSAGE); + } + + const { api, slices = [], requestMiddleware, logger, clientId } = options; + const { reducerPath } = api; + const inject = handle.rootReducer.inject as (slice: unknown) => unknown; + inject(api); + for (const slice of slices) inject(slice); + + const addMiddleware = handle.dynamicMiddleware.addMiddleware as (middleware: unknown) => unknown; + const clients = handle.extra.clients as Record; + if (!(reducerPath in clients)) addMiddleware(api.middleware); + + clients[reducerPath] = { requestMiddleware, logger, clientId }; + handle.store.dispatch(RECOMPUTE_ACTION as never); + + // TypeScript cannot compute state assembled by successive lazy injections. + return handle as unknown as SdkStoreHandle; +} diff --git a/packages/sdk-effects/store/src/lib/store.types.ts b/packages/sdk-effects/store/src/lib/store.types.ts new file mode 100644 index 00000000000..fca24697028 --- /dev/null +++ b/packages/sdk-effects/store/src/lib/store.types.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import type { Middleware, Reducer, Store, ThunkDispatch, UnknownAction } from '@reduxjs/toolkit'; + +/** + * Per-client slot on a store's thunk `extraArgument`. + * + * The fields are intentionally loose: `sdk-store` sits in the effects layer + * alongside `sdk-request-middleware` and `sdk-logger`, so it cannot depend on + * their types without crossing a module boundary. Each client narrows this to + * its own concrete shape at the point of use. + */ +export interface ClientSlot { + readonly requestMiddleware?: readonly unknown[]; + readonly logger?: unknown; + /** + * Identifies which client instance owns this slot, so a second client can be + * detected before it silently shares the first one's cache slice. + */ + readonly clientId?: string; +} + +/** + * The mutable client registry carried as the store's `extraArgument`. + * + * `configureStore` captures this object by reference, so slots added after the + * store is built are visible to every subsequent request. That is what allows a + * second client to attach itself to a store it did not create. + */ +export interface SdkStoreRegistry { + readonly clients: Record; +} + +/** A reducer that supports lazy slice injection, as produced by `combineSlices`. */ +export interface InjectableRootReducer { + inject: (slice: never) => unknown; +} + +/** Middleware chain that accepts additions after the store is created. */ +export interface DynamicMiddleware { + addMiddleware: (...middleware: never[]) => unknown; +} + +/** + * A shared SDK Redux store, independent of the state shape. + * + * This is the single declaration of the contract between client packages, and + * the type that travels between them: `davinci()` and `journey()` expose one, + * `oidc()` accepts one. It is deliberately state-agnostic so a handle typed + * with DaVinci's state is assignable to it without a cast — which is what lets + * the whole path stay cast-free. + * + * An earlier design used a branded interface whose brand existed on no runtime + * object. That forced `as unknown as` on both sides — four unchecked casts to + * move one object across a package boundary, with no compile-time link between + * producer and consumer. A structural type gives real checking instead. + */ +export interface SdkStore { + readonly store: { + getState: () => unknown; + /** `never` parameter keeps any concrete dispatch assignable to this. */ + dispatch: (action: never) => unknown; + subscribe: (listener: () => void) => () => void; + }; + readonly rootReducer: InjectableRootReducer; + readonly dynamicMiddleware: DynamicMiddleware; + readonly extra: SdkStoreRegistry; +} + +/** + * A store handle with a known state shape. + * + * Returned by each client's own store factory so that internal code gets full + * typing on `getState()` and `dispatch()`. Assignable to {@link SdkStore} for + * hand-off to another client. + */ +export interface SdkStoreHandle> extends SdkStore { + /** + * `dispatch` keeps RTK's thunk typing so clients can await the result of + * `endpoint.initiate(...)` exactly as they would on a store they built + * themselves. + */ + readonly store: Store & { + dispatch: ThunkDispatch; + }; + readonly rootReducer: InjectableRootReducer & Reducer; +} + +/** Options describing the client attaching itself to a store. */ +export interface InjectClientOptions { + /** The client's RTK Query api. Its reducer and middleware are both mounted. */ + readonly api: { reducerPath: string; reducer: Reducer; middleware: Middleware }; + /** Additional slices the client owns, e.g. its config and node slices. */ + readonly slices?: readonly { name: string; reducer: Reducer }[]; + readonly requestMiddleware?: readonly unknown[]; + readonly logger?: unknown; + /** Recorded on the slot so repeat injections can be distinguished. */ + readonly clientId?: string; +} diff --git a/packages/sdk-effects/store/src/lib/store.utils.test.ts b/packages/sdk-effects/store/src/lib/store.utils.test.ts new file mode 100644 index 00000000000..2b5266c595f --- /dev/null +++ b/packages/sdk-effects/store/src/lib/store.utils.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { describe, expect, it } from 'vitest'; + +import { clientExtra } from './store.utils.js'; + +interface TestExtra { + requestMiddleware?: string[]; + logger?: string; +} + +describe('clientExtra', () => { + it('returns the slot registered for the requested reducerPath', () => { + // Arrange + const extra = { + clients: { + oidc: { requestMiddleware: ['oidc-mw'], logger: 'oidc-logger' }, + davinci: { requestMiddleware: ['davinci-mw'], logger: 'davinci-logger' }, + }, + }; + + // Act + const result = clientExtra(extra, 'oidc'); + + // Assert + expect(result).toEqual({ requestMiddleware: ['oidc-mw'], logger: 'oidc-logger' }); + }); + + it('never returns another client slot', () => { + // Arrange — this is the §1.1 leak, expressed as a unit property + const extra = { + clients: { + davinci: { requestMiddleware: ['davinci-mw'], logger: 'davinci-logger' }, + }, + }; + + // Act + const result = clientExtra(extra, 'oidc'); + + // Assert + expect(result.requestMiddleware).toBeUndefined(); + expect(result.logger).toBeUndefined(); + }); + + it('returns an empty slot for an unregistered reducerPath', () => { + // Arrange + const extra = { clients: {} }; + + // Act + const result = clientExtra(extra, 'oidc'); + + // Assert + expect(result).toEqual({}); + }); + + it.each([ + ['undefined', undefined], + ['null', null], + ['a non-object', 42], + ['an object without a clients key', { requestMiddleware: ['legacy'] }], + ['an object whose clients value is not an object', { clients: 'nope' }], + ])('returns an empty slot when extra is %s', (_label, extra) => { + // Act + const result = clientExtra(extra, 'oidc'); + + // Assert — must never throw; endpoints call this on every request + expect(result).toEqual({}); + }); + + it('does not expose the registry itself', () => { + // Arrange + const slot = { requestMiddleware: ['oidc-mw'] }; + const extra = { clients: { oidc: slot } }; + + // Act + const result = clientExtra(extra, 'oidc'); + + // Assert + expect(result).toBe(slot); + expect(result).not.toHaveProperty('clients'); + }); +}); diff --git a/packages/sdk-effects/store/src/lib/store.utils.ts b/packages/sdk-effects/store/src/lib/store.utils.ts new file mode 100644 index 00000000000..1824ece4141 --- /dev/null +++ b/packages/sdk-effects/store/src/lib/store.utils.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +import type { ClientSlot, SdkStore, SdkStoreHandle } from './store.types.js'; + +/** Human-readable explanation used whenever an argument fails validation. */ +export const INVALID_STORE_MESSAGE = + 'The provided `store` is not a valid SDK store. Pass the `store` returned by ' + + 'another SDK client, or one created with `createSdkStore()`.'; + +/** Narrows an unknown value to a usable SDK store handle. */ +export function isSdkStoreHandle(value: unknown): value is SdkStore { + if (typeof value !== 'object' || value === null) return false; + + const candidate = value as Partial; + return ( + typeof candidate.store === 'object' && + candidate.store !== null && + typeof candidate.store.dispatch === 'function' && + typeof candidate.store.getState === 'function' && + typeof candidate.store.subscribe === 'function' && + typeof candidate.rootReducer === 'function' && + typeof candidate.rootReducer.inject === 'function' && + typeof candidate.dynamicMiddleware === 'object' && + candidate.dynamicMiddleware !== null && + typeof candidate.dynamicMiddleware.addMiddleware === 'function' && + typeof candidate.extra === 'object' && + candidate.extra !== null && + typeof candidate.extra.clients === 'object' && + candidate.extra.clients !== null + ); +} + +/** Returns an argument error if `store` is supplied but is not an SDK handle. */ +export function assertValidStore( + store: unknown, +): { error: string; type: 'argument_error' } | undefined { + return store !== undefined && !isSdkStoreHandle(store) + ? { error: INVALID_STORE_MESSAGE, type: 'argument_error' } + : undefined; +} + +/** Returns the registered slot for `reducerPath`, if one exists. */ +export function getClientForReducerPath( + store: SdkStore, + reducerPath: string, +): ClientSlot | undefined { + return store.extra.clients[reducerPath]; +} + +/** + * Resolves the calling client's own slot from a store's `extraArgument`. + * + * This runs on every request, so it never throws. An unrecognised or malformed + * `extra` yields the provided `defaults` (or an empty object) rather than an + * error — and, critically, never falls back to a store-wide value that would + * belong to a different client. + * + * When `defaults` are supplied, any slot key that is absent or `undefined` is + * filled in from `defaults`, letting callers express their fallback values once + * at the call site instead of with repeated `?? x` expressions. + * + * @param extra - The thunk `extraArgument`, as received from `api.extra` + * @param reducerPath - The calling api's `reducerPath`, used as the slot key + * @param defaults - Optional fallback values for missing or undefined slot fields + * @returns The client's own slot merged with defaults, or just defaults / {} if no slot is registered + */ +export function clientExtra( + extra: unknown, + reducerPath: string, + defaults?: Partial, +): Slot { + const fallback = (defaults ?? {}) as Slot; + + if (typeof extra !== 'object' || extra === null || !('clients' in extra)) { + return fallback; + } + + const { clients } = extra as { clients: unknown }; + if (typeof clients !== 'object' || clients === null) { + return fallback; + } + + const slot = (clients as Record)[reducerPath]; + if (typeof slot !== 'object' || slot === null) { + return fallback; + } + + if (defaults === undefined) { + return slot as Slot; + } + + // Merge: defaults fill in any key that is absent or undefined in the slot. + const merged = { ...slot } as Record; + for (const [key, value] of Object.entries(defaults as Record)) { + if (merged[key] === undefined) { + merged[key] = value; + } + } + return merged as Slot; +} diff --git a/packages/sdk-effects/store/src/lib/wellknown.api.test.ts b/packages/sdk-effects/store/src/lib/wellknown.api.test.ts new file mode 100644 index 00000000000..cd126216f5e --- /dev/null +++ b/packages/sdk-effects/store/src/lib/wellknown.api.test.ts @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2025 - 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { configureStore } from '@reduxjs/toolkit'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import { createWellknownSelector, wellknownApi, wellknownSelector } from './wellknown.api.js'; + +import type { WellknownResponse } from '@forgerock/sdk-types'; + +const URL_A = 'https://a.example.com/as/.well-known/openid-configuration'; +const URL_B = 'https://b.example.com/as/.well-known/openid-configuration'; + +function wellknownFixture(overrides: Partial = {}) { + return { + issuer: 'https://a.example.com/as', + authorization_endpoint: 'https://a.example.com/as/authorize', + token_endpoint: 'https://a.example.com/as/token', + userinfo_endpoint: 'https://a.example.com/as/userinfo', + jwks_uri: 'https://a.example.com/as/jwks', + ...overrides, + }; +} + +function makeStore() { + return configureStore({ + reducer: { [wellknownApi.reducerPath]: wellknownApi.reducer }, + middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(wellknownApi.middleware), + }); +} + +function jsonResponse(body: unknown, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: { 'Content-Type': 'application/json' }, + }); +} + +describe('wellknownApi', () => { + let fetchSpy: ReturnType; + let calledUrls: string[]; + + beforeEach(() => { + calledUrls = []; + fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { + const url = typeof input === 'string' ? input : (input as Request).url; + calledUrls.push(url); + return jsonResponse(wellknownFixture()); + }) as ReturnType; + }); + + afterEach(() => { + vi.restoreAllMocks(); + wellknownApi.util.resetApiState(); + }); + + it('fetches the configuration exactly once for a single URL', async () => { + // Arrange + const store = makeStore(); + + // Act + const result = await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert + expect(result.data).toMatchObject({ issuer: 'https://a.example.com/as' }); + expect(calledUrls).toHaveLength(1); + }); + + it('serves the second identical request from cache without a second network call', async () => { + // Arrange + const store = makeStore(); + + // Act + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert — this is the property the shared store depends on + expect(calledUrls).toEqual([URL_A]); + }); + + it('keeps a separate cache entry per URL', async () => { + // Arrange + const store = makeStore(); + + // Act + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_B)); + + // Assert + expect(calledUrls).toEqual([URL_A, URL_B]); + expect(wellknownSelector(URL_A, store.getState())).toBeDefined(); + expect(wellknownSelector(URL_B, store.getState())).toBeDefined(); + }); + + it('maps a non-2xx response to an error rather than throwing', async () => { + // Arrange + fetchSpy.mockImplementation(async () => jsonResponse({ message: 'nope' }, 500)); + const store = makeStore(); + + // Act + const result = await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert + expect(result.error).toBeDefined(); + expect(result.data).toBeUndefined(); + }); + + it('maps a structurally invalid well-known payload to an error', async () => { + // Arrange — missing issuer/authorization_endpoint/token_endpoint + fetchSpy.mockImplementation(async () => + jsonResponse({ jwks_uri: 'https://a.example.com/jwks' }), + ); + const store = makeStore(); + + // Act + const result = await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert + expect(result.error).toBeDefined(); + expect(result.data).toBeUndefined(); + }); + + it('maps a network rejection to an error rather than throwing', async () => { + // Arrange + fetchSpy.mockImplementation(async () => { + throw new Error('connection refused'); + }); + const store = makeStore(); + + // Act + const result = await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert + expect(result.error).toBeDefined(); + expect(result.data).toBeUndefined(); + }); +}); + +describe('createWellknownSelector', () => { + it('returns the same selector instance for the same URL', () => { + // Arrange / Act + const first = createWellknownSelector(URL_A); + const second = createWellknownSelector(URL_A); + + // Assert — without this, memoization is rebuilt on every call and never hits + expect(first).toBe(second); + }); + + it('returns distinct selector instances for distinct URLs', () => { + // Arrange / Act + const a = createWellknownSelector(URL_A); + const b = createWellknownSelector(URL_B); + + // Assert + expect(a).not.toBe(b); + }); + + it('does not recompute when called repeatedly against unchanged state', async () => { + // Arrange + vi.spyOn(globalThis, 'fetch').mockImplementation(async () => jsonResponse(wellknownFixture())); + const store = makeStore(); + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + const selector = createWellknownSelector(URL_A); + + // Act + selector(store.getState()); + const afterFirst = selector.recomputations(); + selector(store.getState()); + selector(store.getState()); + + // Assert + expect(selector.recomputations()).toBe(afterFirst); + + vi.restoreAllMocks(); + wellknownApi.util.resetApiState(); + }); +}); + +describe('wellknownSelector', () => { + afterEach(() => { + vi.restoreAllMocks(); + wellknownApi.util.resetApiState(); + }); + + it('returns undefined before the configuration has been fetched', () => { + // Arrange + const store = makeStore(); + + // Act / Assert + expect(wellknownSelector(URL_A, store.getState())).toBeUndefined(); + }); + + it('returns the cached configuration after a successful fetch', async () => { + // Arrange + vi.spyOn(globalThis, 'fetch').mockImplementation(async () => jsonResponse(wellknownFixture())); + const store = makeStore(); + + // Act + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert + expect(wellknownSelector(URL_A, store.getState())).toMatchObject({ + issuer: 'https://a.example.com/as', + }); + }); + + it('returns undefined when the fetch failed', async () => { + // Arrange + vi.spyOn(globalThis, 'fetch').mockImplementation(async () => jsonResponse({}, 500)); + const store = makeStore(); + + // Act + await store.dispatch(wellknownApi.endpoints.configuration.initiate(URL_A)); + + // Assert + expect(wellknownSelector(URL_A, store.getState())).toBeUndefined(); + }); +}); diff --git a/packages/oidc-client/src/lib/wellknown.api.ts b/packages/sdk-effects/store/src/lib/wellknown.api.ts similarity index 75% rename from packages/oidc-client/src/lib/wellknown.api.ts rename to packages/sdk-effects/store/src/lib/wellknown.api.ts index 44a5ba10f82..93814788cee 100644 --- a/packages/oidc-client/src/lib/wellknown.api.ts +++ b/packages/sdk-effects/store/src/lib/wellknown.api.ts @@ -5,10 +5,11 @@ * of the MIT license. See the LICENSE file for details. */ -import { initWellknownQuery } from '@forgerock/sdk-oidc'; import { createSelector } from '@reduxjs/toolkit'; import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'; +import { initWellknownQuery } from './wellknown.effects.js'; + import type { WellknownResponse } from '@forgerock/sdk-types'; import type { FetchBaseQueryError, @@ -16,7 +17,13 @@ import type { QueryReturnValue, } from '@reduxjs/toolkit/query'; -import type { RootState } from './client.types.js'; +/** + * RootState type for wellknown selector consumers. + * Minimal state shape with the wellknown reducer mounted. + */ +export interface WellknownState { + wellknown: ReturnType; +} /** * RTK Query API for well-known endpoint discovery. @@ -45,6 +52,14 @@ export const wellknownApi = createApi({ }), }); +const createWellknownSelectorForUrl = (wellknownUrl: string) => + createSelector( + wellknownApi.endpoints.configuration.select(wellknownUrl), + (result) => result?.data, + ); + +const wellknownSelectors = new Map>(); + /** * Creates a memoized selector for cached well-known data. * @@ -52,10 +67,14 @@ export const wellknownApi = createApi({ * @returns A memoized selector that extracts the WellknownResponse from state, or undefined if not yet fetched */ export function createWellknownSelector(wellknownUrl: string) { - return createSelector( - wellknownApi.endpoints.configuration.select(wellknownUrl), - (result) => result?.data, - ); + const existingSelector = wellknownSelectors.get(wellknownUrl); + if (existingSelector) { + return existingSelector; + } + + const selector = createWellknownSelectorForUrl(wellknownUrl); + wellknownSelectors.set(wellknownUrl, selector); + return selector; } /** @@ -68,7 +87,7 @@ export function createWellknownSelector(wellknownUrl: string) { * @param state - The oidc-client Redux root state * @returns The cached WellknownResponse or undefined if not yet fetched */ -export function wellknownSelector(wellknownUrl: string, state: RootState) { +export function wellknownSelector(wellknownUrl: string, state: WellknownState) { const selector = createSelector( wellknownApi.endpoints.configuration.select(wellknownUrl), (result) => result?.data, diff --git a/packages/sdk-effects/oidc/src/lib/wellknown.effects.test.ts b/packages/sdk-effects/store/src/lib/wellknown.effects.test.ts similarity index 100% rename from packages/sdk-effects/oidc/src/lib/wellknown.effects.test.ts rename to packages/sdk-effects/store/src/lib/wellknown.effects.test.ts diff --git a/packages/sdk-effects/oidc/src/lib/wellknown.effects.ts b/packages/sdk-effects/store/src/lib/wellknown.effects.ts similarity index 100% rename from packages/sdk-effects/oidc/src/lib/wellknown.effects.ts rename to packages/sdk-effects/store/src/lib/wellknown.effects.ts diff --git a/packages/sdk-effects/store/tsconfig.json b/packages/sdk-effects/store/tsconfig.json new file mode 100644 index 00000000000..3a5af05d8ed --- /dev/null +++ b/packages/sdk-effects/store/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "nx": { + "addTypecheckTarget": false + } +} diff --git a/packages/sdk-effects/store/tsconfig.lib.json b/packages/sdk-effects/store/tsconfig.lib.json new file mode 100644 index 00000000000..79c8dd79770 --- /dev/null +++ b/packages/sdk-effects/store/tsconfig.lib.json @@ -0,0 +1,34 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist", + "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", + "emitDeclarationOnly": false, + "module": "nodenext", + "moduleResolution": "nodenext", + "forceConsistentCasingInFileNames": true, + "strict": true, + "importHelpers": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "references": [{ "path": "../../sdk-types/tsconfig.lib.json" }], + "exclude": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx" + ] +} diff --git a/packages/sdk-effects/store/tsconfig.spec.json b/packages/sdk-effects/store/tsconfig.spec.json new file mode 100644 index 00000000000..c8fc6b21fa7 --- /dev/null +++ b/packages/sdk-effects/store/tsconfig.spec.json @@ -0,0 +1,41 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./out-tsc/vitest", + "types": [ + "vitest/globals", + "vitest/importMeta", + "vite/client", + "node", + "vitest" + ], + "module": "nodenext", + "moduleResolution": "nodenext", + "forceConsistentCasingInFileNames": true, + "strict": true, + "importHelpers": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": [ + "vite.config.ts", + "vite.config.mts", + "vitest.config.ts", + "vitest.config.mts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/sdk-effects/store/vite.config.ts b/packages/sdk-effects/store/vite.config.ts new file mode 100644 index 00000000000..57986fd5eae --- /dev/null +++ b/packages/sdk-effects/store/vite.config.ts @@ -0,0 +1,43 @@ +import { defineConfig } from 'vite'; + +export default defineConfig(() => ({ + root: __dirname, + cacheDir: '../../../node_modules/.vite/packages/sdk-effects/store', + plugins: [], + test: { + watch: false, + globals: true, + environment: 'node', + include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + coverage: { + include: ['src/**/*.{js,ts}'], + exclude: [ + 'src/**/*.mock.{js,ts}', + 'src/**/*.data.{js,ts}', + 'src/**/*.test.{js,ts}', + 'coverage/**', + 'dist/**', + '**/node_modules/**', + '**/[.]**', + 'packages/*/test?(s)/**', + '**/*.d.ts', + '**/virtual:*', + '**/__x00__*', + '**/ *', + 'cypress/**', + 'test?(s)/**', + 'test?(-*).?(c|m)[jt]s?(x)', + '**/*{.,-}{test,spec,bench,benchmark}?(-d).?(c|m)[jt]s?(x)', + '**/__tests__/**', + '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*', + '**/vitest.{workspace,projects}.[jt]s?(on)', + '**/.{eslint,mocha,prettier}rc.{?(c|m)js,yml}', + ], + reporter: ['text', 'html', 'json'], + enabled: Boolean(process.env['CI']), + reportsDirectory: './coverage', + provider: 'v8' as const, + }, + }, +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab4fbd9fe98..4785a01e708 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -467,6 +467,9 @@ importers: '@forgerock/sdk-request-middleware': specifier: workspace:* version: link:../sdk-effects/sdk-request-middleware + '@forgerock/sdk-store': + specifier: workspace:* + version: link:../sdk-effects/store '@forgerock/sdk-types': specifier: workspace:* version: link:../sdk-types @@ -517,12 +520,12 @@ importers: '@forgerock/sdk-logger': specifier: workspace:* version: link:../sdk-effects/logger - '@forgerock/sdk-oidc': - specifier: workspace:* - version: link:../sdk-effects/oidc '@forgerock/sdk-request-middleware': specifier: workspace:* version: link:../sdk-effects/sdk-request-middleware + '@forgerock/sdk-store': + specifier: workspace:* + version: link:../sdk-effects/store '@forgerock/sdk-types': specifier: workspace:* version: link:../sdk-types @@ -572,6 +575,9 @@ importers: '@forgerock/sdk-request-middleware': specifier: workspace:* version: link:../sdk-effects/sdk-request-middleware + '@forgerock/sdk-store': + specifier: workspace:* + version: link:../sdk-effects/store '@forgerock/sdk-types': specifier: workspace:* version: link:../sdk-types @@ -642,6 +648,15 @@ importers: specifier: workspace:* version: link:../../sdk-types + packages/sdk-effects/store: + dependencies: + '@forgerock/sdk-types': + specifier: workspace:* + version: link:../../sdk-types + '@reduxjs/toolkit': + specifier: 'catalog:' + version: 2.10.1 + packages/sdk-types: {} packages/sdk-utilities: diff --git a/tsconfig.json b/tsconfig.json index 5bac2012e83..ad278a99c77 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -84,6 +84,9 @@ }, { "path": "./e2e/recognize-app" + }, + { + "path": "./packages/sdk-effects/store" } ] }