Skip to content

Commit 510e93f

Browse files
committed
feat(ping-sdk): standardize sdk configuration
feat(sdk-utilities): add pure unified config validation feat(sdk-utilities): add unified config mapping functions feat(sdk-utilities): export config module from sdk-utilities test(sdk-utilities): add unit tests for config mapping and validation feat(sdk-types): add OIDC authorize params to GetAuthorizationUrlOptions feat(sdk-oidc): wire OIDC authorize params into buildAuthorizeParams feat(oidc-client): add signOutRedirectUri to endSession URL feat(oidc-client): accept unified JSON config in oidc factory feat(journey-client): accept unified JSON config in journey factory feat(davinci-client): accept unified JSON config in davinci factory chore: update api-reports for unified config param widening feat(sdk-utilities): align unified config schema with new journey sub-object refactor(sdk-utilities): mapper functions return ConfigMappingResult with single error fix(sdk-utilities): map log field and use config.log in factories fix(sdk-utilities): tighten isUnifiedSdkConfig discriminant to require object values fix(oidc-client): use throw instead of Promise.reject in unified config error paths refactor(oidc-client): migrate logout.request.test.ts to it + Micro.runPromise pattern docs(oidc-client): add JSDoc to OidcConfig and all new fields fix(sdk): tighten config param types to XConfig | UnifiedSdkConfig test(oidc-client): migrate authorize.request.micros tests to @effect/vitest test(oidc-client): migrate authorize.request.utils tests to @effect/vitest refactor(sdk-utilities): use as const satisfies for required-field constants refactor(sdk-utilities): replace mutable error array with effect Either in config validators refactor(sdk): utility-first config — move json→config transform to sdk-utilities refactor(sdk-utilities): remove LogLevel duplicates — use LogLevel from sdk-types docs(changeset): update sdks-5067 changeset to reflect utility-first config API refactor(sdk-utilities): rename *ConfigFromJson to make*Config refactor(sdk-types): move client config types to sdk-types, delete Mapped* mirrors
1 parent 48279d3 commit 510e93f

38 files changed

Lines changed: 1689 additions & 308 deletions
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
'@forgerock/sdk-utilities': minor
3+
'@forgerock/sdk-types': minor
4+
'@forgerock/sdk-logger': patch
5+
'@forgerock/sdk-oidc': minor
6+
'@forgerock/oidc-client': minor
7+
'@forgerock/journey-client': minor
8+
'@forgerock/davinci-client': minor
9+
---
10+
11+
Add unified cross-platform SDK configuration support
12+
13+
New utility functions in `@forgerock/sdk-utilities` convert the cross-platform unified JSON config schema into each client's native config shape. Validation and mapping are owned entirely by the utilities layer — client factories remain typed to their existing config interfaces.
14+
15+
**New in `@forgerock/sdk-utilities`:**
16+
17+
- `makeOidcConfig(json)` — validates and maps unified JSON → `OidcConfig`; throws on invalid input
18+
- `makeJourneyConfig(json)` — validates and maps unified JSON → `JourneyClientConfig`; throws on invalid input
19+
- `makeDavinciConfig(json)` — validates and maps unified JSON → `DaVinciConfig`; throws on invalid input
20+
- `UnifiedSdkConfig`, `UnifiedOidcConfig`, `UnifiedJourneyConfig` types
21+
- `validateUnifiedSdkConfig` / `validateUnifiedOidcConfig` — pure validation returning `Either<T, ConfigValidationError[]>`
22+
- `unifiedToOidcConfig`, `unifiedToJourneyConfig`, `unifiedToDavinciConfig` — pure mappers returning `Either<T, ConfigValidationError>`
23+
- `isUnifiedSdkConfig` discriminator
24+
- `AuthDisplayValue`, `AuthPromptValue` types (canonical source — shared between `OidcConfig` and `GetAuthorizationUrlOptions`)
25+
26+
**Usage:**
27+
28+
```ts
29+
import { makeDavinciConfig } from '@forgerock/sdk-utilities';
30+
31+
const client = await davinci({ config: makeDavinciConfig(unifiedJsonConfig) });
32+
```
33+
34+
**New in `@forgerock/sdk-types`:**
35+
36+
- `OidcConfig`, `JourneyClientConfig`, `DaVinciConfig` moved here as canonical types (previously mirrored in `sdk-utilities` as `Mapped*` types)
37+
- `AuthDisplayValue`, `AuthPromptValue` types added (renamed from `OidcDisplayValue`/`OidcPromptValue`)
38+
- `GetAuthorizationUrlOptions` extended with `loginHint`, `nonce`, `display`, `uiLocales`, `acrValues`; `prompt` widened to include `'select_account'`
39+
40+
**Updated in `@forgerock/sdk-logger`:**
41+
42+
- `LogLevel` now re-exported from `@forgerock/sdk-types` (single source of truth); runtime behaviour unchanged
43+
44+
**New in `@forgerock/sdk-oidc`:**
45+
46+
- `buildAuthorizeParams` forwards all new OIDC authorize params into the URL
47+
48+
**New in `@forgerock/oidc-client`:**
49+
50+
- `endSession` appends `post_logout_redirect_uri` when `signOutRedirectUri` is set on config
51+
- Authorize URL construction forwards `loginHint`, `state`, `nonce`, `display`, `prompt`, `uiLocales`, `acrValues`, `additionalParameters` from config
52+
53+
**New in `@forgerock/journey-client`:**
54+
55+
- No API change — consume `makeJourneyConfig` at call-site to use unified JSON config
56+
57+
**New in `@forgerock/davinci-client`:**
58+
59+
- No API change — consume `makeDavinciConfig` at call-site to use unified JSON config

packages/davinci-client/api-report/davinci-client.api.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import { ActionCreatorWithPayload } from '@reduxjs/toolkit';
88
import { ActionTypes } from '@forgerock/sdk-request-middleware';
9-
import type { AsyncLegacyConfigOptions } from '@forgerock/sdk-types';
109
import { CustomLogger } from '@forgerock/sdk-logger';
10+
import type { DaVinciConfig } from '@forgerock/sdk-types';
1111
import { FetchBaseQueryError } from '@reduxjs/toolkit/query';
1212
import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query';
1313
import { GenericError } from '@forgerock/sdk-types';
@@ -506,11 +506,7 @@ export type DaVinciCacheEntry = {
506506
// @public (undocumented)
507507
export type DavinciClient = Awaited<ReturnType<typeof davinci>>;
508508

509-
// @public (undocumented)
510-
export interface DaVinciConfig extends AsyncLegacyConfigOptions {
511-
// (undocumented)
512-
responseType?: string;
513-
}
509+
export { DaVinciConfig }
514510

515511
// @public (undocumented)
516512
export interface DaVinciError extends Omit<GenericError, 'error'> {

packages/davinci-client/api-report/davinci-client.types.api.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
import { ActionCreatorWithPayload } from '@reduxjs/toolkit';
88
import { ActionTypes } from '@forgerock/sdk-request-middleware';
9-
import type { AsyncLegacyConfigOptions } from '@forgerock/sdk-types';
109
import { CustomLogger } from '@forgerock/sdk-logger';
10+
import type { DaVinciConfig } from '@forgerock/sdk-types';
1111
import { FetchBaseQueryError } from '@reduxjs/toolkit/query';
1212
import type { FetchBaseQueryMeta } from '@reduxjs/toolkit/query';
1313
import { GenericError } from '@forgerock/sdk-types';
@@ -506,11 +506,7 @@ export type DaVinciCacheEntry = {
506506
// @public (undocumented)
507507
export type DavinciClient = Awaited<ReturnType<typeof davinci>>;
508508

509-
// @public (undocumented)
510-
export interface DaVinciConfig extends AsyncLegacyConfigOptions {
511-
// (undocumented)
512-
responseType?: string;
513-
}
509+
export { DaVinciConfig }
514510

515511
// @public (undocumented)
516512
export interface DaVinciError extends Omit<GenericError, 'error'> {

packages/davinci-client/src/lib/client.store.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
99

1010
import { davinci } from './client.store.js';
11+
import { makeDavinciConfig } from '@forgerock/sdk-utilities';
1112
import type { DaVinciConfig } from './config.types.js';
1213

1314
// ---------------------------------------------------------------------------
@@ -181,3 +182,59 @@ describe('davinci client — cache', () => {
181182
});
182183
});
183184
});
185+
186+
// ---------------------------------------------------------------------------
187+
188+
describe('unified JSON config entry', () => {
189+
beforeEach(() => {
190+
vi.stubGlobal('localStorage', makeStorageStub());
191+
vi.stubGlobal('sessionStorage', makeStorageStub());
192+
mockFetchImplementation();
193+
});
194+
195+
afterEach(() => {
196+
vi.unstubAllGlobals();
197+
vi.restoreAllMocks();
198+
});
199+
200+
it('accepts unified JSON config and initializes successfully', async () => {
201+
const unifiedConfig = {
202+
oidc: {
203+
clientId: '123456789',
204+
discoveryEndpoint: TEST_WELLKNOWN_URL,
205+
scopes: ['openid', 'profile'],
206+
redirectUri: 'https://example.com/callback',
207+
},
208+
};
209+
210+
const client = await davinci({ config: makeDavinciConfig(unifiedConfig) });
211+
expect(client).toHaveProperty('flow');
212+
expect(client).toHaveProperty('subscribe');
213+
});
214+
215+
it('throws when unified JSON config has missing required field', async () => {
216+
const invalidConfig = {
217+
oidc: {
218+
// clientId missing
219+
discoveryEndpoint: TEST_WELLKNOWN_URL,
220+
scopes: ['openid'],
221+
redirectUri: 'https://example.com/callback',
222+
},
223+
};
224+
225+
expect(() => makeDavinciConfig(invalidConfig)).toThrow(/Invalid unified SDK config/);
226+
});
227+
228+
it('throws when unified JSON config has wrong field type', async () => {
229+
const invalidConfig = {
230+
oidc: {
231+
clientId: '123',
232+
discoveryEndpoint: TEST_WELLKNOWN_URL,
233+
scopes: 'openid', // should be array
234+
redirectUri: 'https://example.com/callback',
235+
},
236+
};
237+
238+
expect(() => makeDavinciConfig(invalidConfig)).toThrow(/Invalid unified SDK config/);
239+
});
240+
});

packages/davinci-client/src/lib/client.store.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ export async function davinci<ActionType extends ActionTypes = ActionTypes>({
7777
custom?: CustomLogger;
7878
};
7979
}) {
80-
const log = loggerFn({ level: logger?.level || 'error', custom: logger?.custom });
80+
const log = loggerFn({
81+
level: logger?.level ?? config.log ?? 'error',
82+
custom: logger?.custom,
83+
});
8184
const store = createClientStore({ requestMiddleware, logger: log });
8285
const serverInfo = createStorage<ContinueNode['server']>({
8386
type: 'localStorage',

packages/davinci-client/src/lib/config.types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
* of the MIT license. See the LICENSE file for details.
66
*/
77

8-
import type { AsyncLegacyConfigOptions, WellknownResponse } from '@forgerock/sdk-types';
8+
import type { WellknownResponse } from '@forgerock/sdk-types';
9+
import type { DaVinciConfig } from '@forgerock/sdk-types';
910

10-
export interface DaVinciConfig extends AsyncLegacyConfigOptions {
11-
responseType?: string;
12-
}
11+
export type { DaVinciConfig };
1312

1413
export interface InternalDaVinciConfig extends DaVinciConfig {
1514
wellknownResponse: WellknownResponse;

packages/journey-client/api-report/journey-client.api.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
```ts
66

77
import { ActionTypes } from '@forgerock/sdk-request-middleware';
8-
import type { AsyncLegacyConfigOptions } from '@forgerock/sdk-types';
98
import { AuthResponse } from '@forgerock/sdk-types';
109
import { Callback } from '@forgerock/sdk-types';
1110
import { CallbackType } from '@forgerock/sdk-types';
@@ -16,6 +15,8 @@ import { FailedPolicyRequirement } from '@forgerock/sdk-types';
1615
import { FailureDetail } from '@forgerock/sdk-types';
1716
import { GenericError } from '@forgerock/sdk-types';
1817
import { isValidWellknownUrl } from '@forgerock/sdk-utilities';
18+
import { JourneyClientConfig } from '@forgerock/sdk-types';
19+
import { JourneyServerConfig } from '@forgerock/sdk-types';
1920
import { LogLevel } from '@forgerock/sdk-logger';
2021
import { NameValue } from '@forgerock/sdk-types';
2122
import { PolicyKey } from '@forgerock/sdk-types';
@@ -202,11 +203,7 @@ export interface JourneyClient {
202203
}) => Promise<void | GenericError>;
203204
}
204205

205-
// @public
206-
export interface JourneyClientConfig extends AsyncLegacyConfigOptions {
207-
// (undocumented)
208-
serverConfig: JourneyServerConfig;
209-
}
206+
export { JourneyClientConfig }
210207

211208
// @public (undocumented)
212209
export type JourneyLoginFailure = AuthResponse & {
@@ -231,11 +228,7 @@ export type JourneyLoginSuccess = AuthResponse & {
231228
// @public
232229
export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError;
233230

234-
// @public
235-
export interface JourneyServerConfig {
236-
timeout?: number;
237-
wellknown: string;
238-
}
231+
export { JourneyServerConfig }
239232

240233
// @public (undocumented)
241234
export type JourneyStep = AuthResponse & {

packages/journey-client/api-report/journey-client.types.api.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
```ts
66

77
import { ActionTypes } from '@forgerock/sdk-request-middleware';
8-
import type { AsyncLegacyConfigOptions } from '@forgerock/sdk-types';
98
import { AuthResponse } from '@forgerock/sdk-types';
109
import { Callback } from '@forgerock/sdk-types';
1110
import { CallbackType } from '@forgerock/sdk-types';
@@ -15,6 +14,8 @@ import { FailedPolicyRequirement } from '@forgerock/sdk-types';
1514
import { FailureDetail } from '@forgerock/sdk-types';
1615
import { GenericError } from '@forgerock/sdk-types';
1716
import { isValidWellknownUrl } from '@forgerock/sdk-utilities';
17+
import { JourneyClientConfig } from '@forgerock/sdk-types';
18+
import { JourneyServerConfig } from '@forgerock/sdk-types';
1819
import { LogLevel } from '@forgerock/sdk-logger';
1920
import { NameValue } from '@forgerock/sdk-types';
2021
import { PolicyKey } from '@forgerock/sdk-types';
@@ -189,11 +190,7 @@ export interface JourneyClient {
189190
}) => Promise<void | GenericError>;
190191
}
191192

192-
// @public
193-
export interface JourneyClientConfig extends AsyncLegacyConfigOptions {
194-
// (undocumented)
195-
serverConfig: JourneyServerConfig;
196-
}
193+
export { JourneyClientConfig }
197194

198195
// @public (undocumented)
199196
export type JourneyLoginFailure = AuthResponse & {
@@ -218,11 +215,7 @@ export type JourneyLoginSuccess = AuthResponse & {
218215
// @public
219216
export type JourneyResult = JourneyStep | JourneyLoginSuccess | JourneyLoginFailure | GenericError;
220217

221-
// @public
222-
export interface JourneyServerConfig {
223-
timeout?: number;
224-
wellknown: string;
225-
}
218+
export { JourneyServerConfig }
226219

227220
// @public (undocumented)
228221
export type JourneyStep = AuthResponse & {

packages/journey-client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"@forgerock/sdk-utilities": "workspace:*",
4040
"@forgerock/storage": "workspace:*",
4141
"@reduxjs/toolkit": "catalog:",
42+
"effect": "catalog:effect",
4243
"tslib": "catalog:"
4344
},
4445
"devDependencies": {

packages/journey-client/src/lib/client.store.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
1212
import type { GenericError, Step, WellknownResponse } from '@forgerock/sdk-types';
1313

1414
import { journey } from './client.store.js';
15+
import { makeJourneyConfig } from '@forgerock/sdk-utilities';
1516
import { createJourneyStep } from './step.utils.js';
1617
import { JourneyClientConfig } from './config.types.js';
1718

@@ -502,4 +503,46 @@ describe('journey-client', () => {
502503
expect(request.url).toBe('https://test.com/am/json/realms/root/realms/alpha/authenticate');
503504
});
504505
});
506+
507+
describe('unified JSON config entry', () => {
508+
test('accepts unified JSON config and initializes successfully', async () => {
509+
setupMockFetch();
510+
511+
const unifiedConfig = {
512+
oidc: {
513+
clientId: 'ignored-by-journey',
514+
discoveryEndpoint: mockWellknownUrl,
515+
scopes: ['openid'],
516+
redirectUri: 'https://example.com/callback',
517+
},
518+
};
519+
520+
const client = await journey({ config: makeJourneyConfig(unifiedConfig) });
521+
expect(client).toHaveProperty('start');
522+
expect(client).toHaveProperty('next');
523+
});
524+
525+
test('throws when unified JSON config has missing required field', async () => {
526+
const invalidConfig = {
527+
oidc: {
528+
// discoveryEndpoint missing — required even for journey
529+
},
530+
};
531+
532+
expect(() => makeJourneyConfig(invalidConfig)).toThrow(/Invalid unified SDK config/);
533+
});
534+
535+
test('throws when unified JSON config has wrong field type', async () => {
536+
const invalidConfig = {
537+
oidc: {
538+
clientId: '123',
539+
discoveryEndpoint: mockWellknownUrl,
540+
scopes: 'openid', // should be array
541+
redirectUri: 'https://example.com/callback',
542+
},
543+
};
544+
545+
expect(() => makeJourneyConfig(invalidConfig)).toThrow(/Invalid unified SDK config/);
546+
});
547+
});
505548
});

0 commit comments

Comments
 (0)