diff --git a/javascript/reactjs-todo-davinci/README.md b/javascript/reactjs-todo-davinci/README.md index 9f9d7c11..e4bb7707 100644 --- a/javascript/reactjs-todo-davinci/README.md +++ b/javascript/reactjs-todo-davinci/README.md @@ -11,12 +11,20 @@ This sample code is provided "as is" and is not a supported product of Ping Iden - SingleSelectCollector - ReadOnlyCollector - PhoneNumberCollector +- PhoneNumberExtensionCollector - DeviceRegistrationCollector - DeviceAuthenticationCollector +- FidoRegistrationCollector +- FidoAuthenticationCollector - IdpCollector - SubmitCollector - FlowCollector - ProtectCollector +- QrCodeCollector +- RichTextCollector +- BooleanCollector +- ValidatedBooleanCollector +- PollingCollector ## Requirements diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/boolean.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/boolean.js new file mode 100644 index 00000000..5bdc60e5 --- /dev/null +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/boolean.js @@ -0,0 +1,50 @@ +/* + * ping-sample-web-react-davinci + * + * boolean.js + * + * Copyright (c) 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 React, { useState } from 'react'; +import { interpolateRichContent } from '../utilities/rich-content'; + +export default function BooleanComponent({ collector, inputName, updater }) { + const [isChecked, setIsChecked] = useState(collector.output.value ?? false); + + const fieldId = collector.output.key || `${inputName}-checkbox-field`; + + const { richContent } = collector.output; + const label = richContent?.replacements?.length + ? interpolateRichContent(richContent) + : collector.output.label || ''; + + const required = collector.input.validation?.some( + (validation) => validation.type === 'required' && validation.rule === true, + ); + + const handleChange = (event) => { + const value = event.target.checked; + setIsChecked(value); + updater(value); + }; + + return ( +
+ + +
+ ); +} diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/device.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/device.js new file mode 100644 index 00000000..af55bfd1 --- /dev/null +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/device.js @@ -0,0 +1,54 @@ +/* + * ping-sample-web-react-davinci + * + * device.js + * + * Copyright (c) 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 React, { useEffect, useContext, useState } from 'react'; +import { ThemeContext } from '../../context/theme.context.js'; + +export default function DeviceComponent({ collector, updater }) { + const [selectedDevice, setSelectedDevice] = useState( + collector.output.options?.[0]?.value ?? null, + ); + const theme = useContext(ThemeContext); + + useEffect(() => { + if (selectedDevice) { + const result = updater(selectedDevice); + if (result && 'error' in result) { + console.error('Failed to update device', result.error); + } + } + }, [selectedDevice, updater]); + + return ( +
+ + + +
+ ); +} diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/error.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/error.js index cdb42381..37aec2c2 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/error.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/error.js @@ -1,3 +1,13 @@ +/* + * ping-sample-web-react-davinci + * + * error.js + * + * 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 React, { useEffect, useState } from 'react'; export default function Error({ getError }) { diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js index 6a87624d..c54c6580 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/form.js @@ -9,16 +9,21 @@ */ import React, { Fragment, useContext, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import Readonly from './readonly.js'; +import ReadOnly from './readonly.js'; import Text from './text.js'; import Error from './error.js'; import Password from './password.js'; import SocialLoginButton from './social-login-button.js'; import SubmitButton from './submit-button.js'; import Protect from './protect.js'; -import ObjectValueComponent from './object-value.js'; +import PhoneNumberComponent from './phone-number.js'; +import DeviceComponent from './device.js'; import SingleSelect from './single-select.js'; import FlowLink from './flow-link.js'; +import FidoComponent from './fido.js'; +import PollingComponent from './polling.js'; +import BooleanComponent from './boolean.js'; +import QrCode from './qr-code.js'; import Unknown from './unknown.js'; import Alert from './alert.js'; import KeyIcon from '../icons/key-icon'; @@ -30,7 +35,6 @@ import { ProtectContext } from '../../context/protect.context.js'; import { ThemeContext } from '../../context/theme.context.js'; import useDavinci from './hooks/davinci.hook.js'; import useOAuth from './hooks/oauth.hook.js'; -import FidoComponent from './fido.js'; /** * @function Form - React view for managing the user authentication journey @@ -58,7 +62,7 @@ export default function Form() { const [user, setCode] = useOAuth(); const [ { formName, formAction, node, collectors }, - { getError, setNext, startNewFlow, updater, externalIdp }, + { getError, setNext, startNewFlow, updater, pollStatus, externalIdp }, ] = useDavinci(); /** @@ -169,17 +173,35 @@ export default function Form() { case 'ERROR_DISPLAY': return ; case 'ReadOnlyCollector': - return ; + case 'RichTextCollector': + return ; + case 'BooleanCollector': + case 'ValidatedBooleanCollector': + return ( + + ); case 'PhoneNumberCollector': + case 'PhoneNumberExtensionCollector': + return ( + + ); case 'DeviceRegistrationCollector': case 'DeviceAuthenticationCollector': return ( - ); case 'IdpCollector': @@ -201,8 +223,21 @@ export default function Form() { submitForm={setNext} /> ); + case 'PollingCollector': + return ( + + ); case 'ProtectCollector': return ; + case 'QrCodeCollector': + return ; case 'SubmitCollector': return ; case 'FlowCollector': diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/hooks/davinci.hook.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/hooks/davinci.hook.js index ab431f72..5a9ae8e9 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/hooks/davinci.hook.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/hooks/davinci.hook.js @@ -94,6 +94,14 @@ export default function useDavinci() { return davinciClient.update(collector); } + /** + * @function pollStatus - Gets the DaVinci client pollStatus function for a collector + * @returns {function} - A function to start challenge or continue polling + */ + function pollStatus(collector) { + return davinciClient.pollStatus(collector); + } + /** * @function setNext - Get the next node in the DaVinci flow * @returns {Promise} @@ -148,6 +156,7 @@ export default function useDavinci() { setNext, startNewFlow, updater, + pollStatus, externalIdp: davinciClient && davinciClient.externalIdp(), getError: davinciClient && davinciClient.getError, }, diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/object-value.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/object-value.js deleted file mode 100644 index 1006bbdd..00000000 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/object-value.js +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useEffect, useContext, useState } from 'react'; -import { ThemeContext } from '../../context/theme.context.js'; - -export default function ObjectValueComponent({ collector, updater }) { - const [selected, setSelected] = useState(collector.output.options[0].value); - const theme = useContext(ThemeContext); - - useEffect(() => { - updater(selected); - }, [selected]); - - const handleChange = (event) => { - event.preventDefault(); - setSelected(event.target.value); - }; - if ( - collector.type === 'DeviceAuthenticationCollector' || - collector.type === 'DeviceRegistrationCollector' - ) { - return ( -
- - - -
- ); - } else if (collector.type === 'PhoneNumberCollector') { - return ( - <> - - { - const selectedValue = event.target.value; - if (!selectedValue) { - console.error('No value found for the selected option'); - return; - } - updater({ phoneNumber: selectedValue, countryCode: collector.input.value.countryCode }); - }} - /> - - ); - } else { - return null; // Or handle other collector types, if applicable - } -} diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/phone-number.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/phone-number.js new file mode 100644 index 00000000..b5a62675 --- /dev/null +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/phone-number.js @@ -0,0 +1,106 @@ +/* + * ping-sample-web-react-davinci + * + * phone-number.js + * + * Copyright (c) 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 React, { useState } from 'react'; + +export default function PhoneNumberComponent({ collector, updater, inputName }) { + const [phoneValue, setPhoneValue] = useState({ + phoneNumber: collector.input.value?.phoneNumber ?? '', + extension: collector.input.value?.extension ?? '', + }); + + if (collector.type === 'PhoneNumberCollector') { + const phoneInputId = `${inputName}-phone-number`; + const required = collector.input.validation?.some( + (validation) => validation.type === 'required' && validation.rule === true, + ); + + return ( + <> + + { + const updatedPhone = event.target.value; + setPhoneValue((prev) => ({ ...prev, phoneNumber: updatedPhone })); + updater({ + phoneNumber: updatedPhone, + countryCode: collector.input.value?.countryCode, + }); + }} + required={required} + /> + + ); + } else if (collector.type === 'PhoneNumberExtensionCollector') { + const phoneInputId = `${inputName}-phone-number`; + const extensionInputId = `${inputName}-extension`; + const required = collector.input.validation?.some( + (validation) => validation.type === 'required' && validation.rule === true, + ); + return ( + <> + + { + const updatedPhoneNumber = event.target.value; + const updatedPhoneValue = { ...phoneValue, phoneNumber: updatedPhoneNumber }; + setPhoneValue(updatedPhoneValue); + updater({ + phoneNumber: updatedPhoneValue.phoneNumber, + countryCode: collector.input.value?.countryCode, + extension: updatedPhoneValue.extension, + }); + }} + required={required} + /> + + { + const updatedExtension = event.target.value; + const updatedPhoneValue = { ...phoneValue, extension: updatedExtension }; + setPhoneValue(updatedPhoneValue); + updater({ + phoneNumber: updatedPhoneValue.phoneNumber, + countryCode: collector.input.value?.countryCode, + extension: updatedPhoneValue.extension, + }); + }} + required={required} + /> + + ); + } else { + return null; + } +} diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/polling.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/polling.js new file mode 100644 index 00000000..e7a13899 --- /dev/null +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/polling.js @@ -0,0 +1,79 @@ +/* + * ping-sample-web-react-davinci + * + * polling.js + * + * Copyright (c) 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 React, { useState, useEffect } from 'react'; + +import Loading from '../utilities/loading.js'; + +/** + * @function PollingComponent - React component for DaVinci PollingCollector + * @param {Object} props + * @param {Object} props.collector - PollingCollector + * @param {Function} props.pollStatus - Poller function returning the current poll status + * @param {Function} props.updater - Updater bound to the PollingCollector + * @param {Function} props.submitForm - Function to advance the DaVinci flow + * @returns {Object} - React component + */ +export default function PollingComponent({ collector, cacheKey, pollStatus, updater, submitForm }) { + const [isPolling, setIsPolling] = useState(false); + const [error, setError] = useState(null); + const [result, setResult] = useState(null); + + useEffect(() => { + async function handlePoll() { + setIsPolling(true); + setError(null); + setResult(null); + + const status = await pollStatus(); + + if (typeof status !== 'string' && status && 'error' in status) { + const message = status.error?.message || 'Polling error'; + console.error(message); + setError(message); + setIsPolling(false); + return; + } + + const updateResult = updater(status); + if (updateResult && 'error' in updateResult) { + const message = updateResult.error?.message || 'Polling error'; + console.error(message); + setError(message); + setIsPolling(false); + return; + } + + setResult(status); + await submitForm(); + setIsPolling(false); + } + + handlePoll(); + + /** + * In continue polling mode, a rewind node can be returned if polling should continue. + * While the polling collector in this node looks identical, the node's cache key will + * differ indicating a re-render/re-poll should occur. + */ + }, [cacheKey]); + + return ( +
+ {isPolling && } + {result &&

{`Polling result: ${result}`}

} + {error && ( +

+ {`Polling error: ${error}`} +

+ )} +
+ ); +} diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/qr-code.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/qr-code.js new file mode 100644 index 00000000..9fc2a30f --- /dev/null +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/qr-code.js @@ -0,0 +1,31 @@ +/* + * ping-sample-web-react-davinci + * + * qr-code.js + * + * Copyright (c) 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 React from 'react'; + +export default function QrCode({ collector }) { + if (collector.error) { + return ( +

+ {`QR Code error: ${collector.error}`} +

+ ); + } + + return ( +
+ QR Code + {collector.output.label && ( +

+ {`Manual code: ${collector.output.label}`} +

+ )} +
+ ); +} diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/readonly.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/readonly.js index 936963c8..45a4f15e 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/readonly.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/readonly.js @@ -1,9 +1,37 @@ +/* + * ping-sample-web-react-davinci + * + * readonly.js + * + * 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 React from 'react'; +import { interpolateRichContent } from '../utilities/rich-content'; + +export default function ReadOnly({ collector }) { + const collectorType = collector.type; + const output = collector.output; + + if (collectorType === 'ReadOnlyCollector') { + return ( + <> + {/* Display agreement title if it exists */} + {output.title &&

{output.title}

} +

{output.content}

+ + ); + } else if (collectorType === 'RichTextCollector') { + const { richContent } = output; + + if (!richContent?.replacements?.length) { + return

{output.content}

; + } -export default function Readonly({ collector }) { - return ( -
-

{collector.output.label}

-
- ); + return

{interpolateRichContent(richContent)}

; + } else { + return null; + } } diff --git a/javascript/reactjs-todo-davinci/client/components/davinci-client/single-select.js b/javascript/reactjs-todo-davinci/client/components/davinci-client/single-select.js index 3ce1737b..4ddffd63 100644 --- a/javascript/reactjs-todo-davinci/client/components/davinci-client/single-select.js +++ b/javascript/reactjs-todo-davinci/client/components/davinci-client/single-select.js @@ -1,3 +1,13 @@ +/* + * ping-sample-web-react-davinci + * + * single-select.js + * + * 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 React, { useState } from 'react'; export default function SingleSelectComponent({ collector, collectorName, updater }) { @@ -13,7 +23,7 @@ export default function SingleSelectComponent({ collector, collectorName, update }; return ( -
+
diff --git a/javascript/reactjs-todo-davinci/client/components/utilities/rich-content.js b/javascript/reactjs-todo-davinci/client/components/utilities/rich-content.js new file mode 100644 index 00000000..10930703 --- /dev/null +++ b/javascript/reactjs-todo-davinci/client/components/utilities/rich-content.js @@ -0,0 +1,69 @@ +/* + * ping-sample-web-react-davinci + * + * rich-content.js + * + * Copyright (c) 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 React from 'react'; + +export function interpolateRichContent(richContent) { + /** + * Split the content on `{{key}}` placeholders. Because the regex has a + * capture group, `split` interleaves results: even indices are literal + * text, odd indices are replacement keys. + * + * Sample rich content: + * { + * "content": "A translatable rich text link to {{link1}}.", + * "replacements": [ + * { + * "key": "link1", + * "type": "link", + * "value": "Ping Identity", + * "href": "https://www.pingidentity.com", + * "target": "_self" + * } + * ] + * } + */ + const segments = richContent.content.split(/\{\{(\w+)\}\}/); + + // Index replacements by key so each placeholder can be looked up in O(1). + const replacementMap = new Map(richContent.replacements.map((r) => [r.key, r])); + + return ( + <> + {segments.map((segment, i) => { + // Even index → plain text segment. Empty strings render as `null` + // so React skips them instead of emitting empty text nodes. + if (i % 2 === 0) { + return segment || null; + } + + // Odd index → placeholder key. Only "link" types are currently supported. + // Skip anything that isn't a link. + const replacement = replacementMap.get(segment); + if (replacement?.type !== 'link') { + return null; + } + + // For external links, pair `target="_blank"` with `rel="noopener noreferrer"` + // to prevent the new page from accessing `window.opener`. + const isBlank = replacement.target === '_blank'; + return ( + + {replacement.value} + + ); + })} + + ); +} diff --git a/javascript/reactjs-todo-davinci/client/styles/README.md b/javascript/reactjs-todo-davinci/client/styles/README.md index b5452079..b7ecd23d 100644 --- a/javascript/reactjs-todo-davinci/client/styles/README.md +++ b/javascript/reactjs-todo-davinci/client/styles/README.md @@ -1,3 +1,3 @@ # Styles -Here's where we pull in all the relevant Bootstrap components, provide are custom variables and our custom styles. If we can't customize a Bootstrap component via variables, then we add a custom CSS class to override the default styling. These classes are appended with `cstm_`. This ensure explicit communication that we are overriding this component with a custom class. +Here's where we pull in all the relevant Bootstrap components, provide our custom variables and our custom styles. If we can't customize a Bootstrap component via variables, then we add a custom CSS class to override the default styling. These classes are appended with `cstm_`. This ensures explicit communication that we are overriding this component with a custom class. diff --git a/javascript/reactjs-todo-davinci/package.json b/javascript/reactjs-todo-davinci/package.json index 325d7b06..590d05b1 100644 --- a/javascript/reactjs-todo-davinci/package.json +++ b/javascript/reactjs-todo-davinci/package.json @@ -39,7 +39,7 @@ "webpack-dev-server": "^5.1.0" }, "dependencies": { - "@forgerock/davinci-client": "latest", + "@forgerock/davinci-client": "^0.0.0-beta-20260623002246", "@forgerock/oidc-client": "latest", "@forgerock/protect": "latest", "cookie-parser": "^1.4.5", diff --git a/package-lock.json b/package-lock.json index f536283c..ca0dfa7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4121,7 +4121,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@forgerock/davinci-client": "latest", + "@forgerock/davinci-client": "^0.0.0-beta-20260623002246", "@forgerock/oidc-client": "latest", "@forgerock/protect": "latest", "cookie-parser": "^1.4.5", @@ -4170,6 +4170,73 @@ "webpack-dev-server": "^5.1.0" } }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/davinci-client": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/davinci-client/-/davinci-client-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-tTHDM3Xj3IO0nDL/i2osuDKmYrZztmLKC+siVlD+ApZe+li+16jSo5mvR0fCCs7omRfQvkNdS5Yrc5dE0juqpA==", + "license": "MIT", + "dependencies": { + "@forgerock/sdk-logger": "0.0.0-beta-20260623002246", + "@forgerock/sdk-oidc": "0.0.0-beta-20260623002246", + "@forgerock/sdk-request-middleware": "0.0.0-beta-20260623002246", + "@forgerock/sdk-types": "0.0.0-beta-20260623002246", + "@forgerock/sdk-utilities": "0.0.0-beta-20260623002246", + "@forgerock/storage": "0.0.0-beta-20260623002246", + "@reduxjs/toolkit": "^2.8.2", + "effect": "^3.20.0", + "immer": "^10.1.1" + } + }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/sdk-logger": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/sdk-logger/-/sdk-logger-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-mQ5vIXBlz/F/BSqv6p1Yhj6QGapkZHgjRjAY77tQP7vsN6OgmpdDDgEhJPQUSWee6VRQRHVCHxx94HXi7GrPaw==", + "license": "MIT" + }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/sdk-oidc": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/sdk-oidc/-/sdk-oidc-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-Q25Oo9csWI7NkWxgHyQr3p9PCaYRrSh/VoNDaxgD66lHDktIQtdoYFktx9hGWWHMtO9HJMR7uficKBe6fDeJSA==", + "license": "MIT", + "dependencies": { + "@forgerock/sdk-types": "0.0.0-beta-20260623002246", + "@forgerock/sdk-utilities": "0.0.0-beta-20260623002246" + } + }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/sdk-request-middleware": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/sdk-request-middleware/-/sdk-request-middleware-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-aiiNAOC0ssQn2gFKKpyQx1NHvL8gGYRCOD5jRoVQuKJkh09/i6Zc6v90snPfBBqq0HjnBjiXx9Lj7vct1NvWnA==", + "license": "MIT", + "dependencies": { + "@reduxjs/toolkit": "^2.8.2" + } + }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/sdk-types": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/sdk-types/-/sdk-types-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-4SwFn/b5tt0BRIjQn8d+0ubgzyRfhJ9S/RjWmX009UEoUWcNLzwURW6OfIMk4zDOT/QdBFxzPAfJgqmqEGVZJg==", + "license": "MIT" + }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/sdk-utilities": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/sdk-utilities/-/sdk-utilities-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-JL5kbvZNxitfXWRxug6YyHbEmqGtdhtDy35HlTuRc32dYmLHGWSeSypJID0YDbBa1WiT+HuK2Axwkw+wF/u/qA==", + "license": "MIT", + "dependencies": { + "@forgerock/sdk-types": "0.0.0-beta-20260623002246", + "effect": "^3.20.0" + } + }, + "javascript/reactjs-todo-davinci/node_modules/@forgerock/storage": { + "version": "0.0.0-beta-20260623002246", + "resolved": "http://localhost:4873/@forgerock/storage/-/storage-0.0.0-beta-20260623002246.tgz", + "integrity": "sha512-jYVKvjeUyb06/P3drpVsOk3H35VCD8wD590L3IcHvuDDhEjjVUbrMCFfaOM1AcNu3UtbBvMkPnSKvjI0VFsuKg==", + "license": "MIT", + "dependencies": { + "@forgerock/sdk-types": "0.0.0-beta-20260623002246" + } + }, "javascript/reactjs-todo-davinci/node_modules/eslint-config-prettier": { "version": "8.10.2", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.2.tgz", @@ -9134,17 +9201,18 @@ }, "node_modules/@forgerock/davinci-client": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/davinci-client/-/davinci-client-2.0.0.tgz", - "integrity": "sha512-xU2TbMU7Iqs8fD7zGEm6df+D5v2bUmVPS3ptpRZHFIFoLMiLbvnD0XmxdjjKfX2brNlubw5b6BeO8OFI+v73Ww==", + "resolved": "https://pkg.pr.new/@forgerock/davinci-client@638", + "integrity": "sha512-GqXGqGrWv8ID0VIZ06C1NWrO4x6oDUQA7gDJTITgTRrp8VHymoOlS5VUmw24r6oqPTdZu+kKQ/Yvhftv7se2MA==", + "license": "MIT", "dependencies": { - "@forgerock/sdk-logger": "2.0.0", - "@forgerock/sdk-oidc": "2.0.0", - "@forgerock/sdk-request-middleware": "2.0.0", - "@forgerock/sdk-types": "2.0.0", - "@forgerock/sdk-utilities": "2.0.0", - "@forgerock/storage": "2.0.0", + "@forgerock/sdk-logger": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-logger@3abf436", + "@forgerock/sdk-oidc": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-oidc@3abf436", + "@forgerock/sdk-request-middleware": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-request-middleware@3abf436", + "@forgerock/sdk-types": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@3abf436", + "@forgerock/sdk-utilities": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-utilities@3abf436", + "@forgerock/storage": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/storage@3abf436", "@reduxjs/toolkit": "^2.8.2", - "effect": "^3.19.0", + "effect": "^3.20.0", "immer": "^10.1.1" } }, @@ -9214,45 +9282,51 @@ }, "node_modules/@forgerock/sdk-logger": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/sdk-logger/-/sdk-logger-2.0.0.tgz", - "integrity": "sha512-H1MvYXM17v/IvolBdzFxCN1ZUq3+P/IOXBQA93Yma4CEBRzdzOUlusisQjAjsbm0kjTqv77EaW5j73belD0Qcg==" + "resolved": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-logger@3abf436", + "integrity": "sha512-nVW6aCP6RAA11E+d63vsR+mwHvects08cS3prJUKMhxjSY/EUxQlnTDI+qAtXUOquhdoRWFiHpNVNWK67heYoA==", + "license": "MIT" }, "node_modules/@forgerock/sdk-oidc": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/sdk-oidc/-/sdk-oidc-2.0.0.tgz", - "integrity": "sha512-5xj1mkvXZzhT0uZD4pGswznq4Ot7fPNtoIm7X5p2k7hoA8yibaoNadHrFmXZPFSGvomKDxbsmvNA6NajDu5nIQ==", + "resolved": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-oidc@3abf436", + "integrity": "sha512-Uuh7rH3BpHEJ5yV+ho4+dQBCaO8/c+b1al+jQmK4QF8KRukLoXj7PCzIIZ3mIyYr9/R+2/iWy5pJEaSb8Brqiw==", + "license": "MIT", "dependencies": { - "@forgerock/sdk-types": "2.0.0", - "@forgerock/sdk-utilities": "2.0.0" + "@forgerock/sdk-types": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@3abf436", + "@forgerock/sdk-utilities": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-utilities@3abf436" } }, "node_modules/@forgerock/sdk-request-middleware": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/sdk-request-middleware/-/sdk-request-middleware-2.0.0.tgz", - "integrity": "sha512-0ZAV0Lviq662qrPAPHE+RPr7my29hc7n5F6OkNLLW3DONl94+nq3Dl3M5D1fezkfM2bdOQmJPaHwmq/7hwsTYg==", + "resolved": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-request-middleware@3abf436", + "integrity": "sha512-oZIRX2oXiFyAfarMC35ucXX4IHFSQhmcT95btB2LWFLaFDBuwEMF6d+sxb35VzP9XN1LF5ckDTF+lIaXCc2MiA==", + "license": "MIT", "dependencies": { "@reduxjs/toolkit": "^2.8.2" } }, "node_modules/@forgerock/sdk-types": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/sdk-types/-/sdk-types-2.0.0.tgz", - "integrity": "sha512-my19sUM3noqsEIjktq8Vyf0iOMZir5tbB+y/b6I5pTDRtvTjznW9wHmAjs6Z+SGeJfyj9Un/LaxOXcCH9bYwrA==" + "resolved": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@3abf436", + "integrity": "sha512-8xJozWN6wL1SOpmT5fYmUST+QS2YYBvehvIUn6NHPYsHPNTvD6/l00KofbESAaMXtJjY2uwqpISiL1sPHHquww==", + "license": "MIT" }, "node_modules/@forgerock/sdk-utilities": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/sdk-utilities/-/sdk-utilities-2.0.0.tgz", - "integrity": "sha512-nLo9oB6xZbkbdxtQU+sYsb4+jcJv506x0udvlZOdbkfVQe1LgvZzQi6q6jghiXkHZPIJ79Z7DwLOFNiKxTdZGA==", + "resolved": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-utilities@3abf436", + "integrity": "sha512-jPMX/vXaiy1zYOGvlyc8+Y9M3CFABF55JJD7t5eGLcikMe1NtCcBqYLqL1gcI//B7gX/pl7mKHFt5MoQTVgU8w==", + "license": "MIT", "dependencies": { - "@forgerock/sdk-types": "2.0.0" + "@forgerock/sdk-types": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@3abf436" } }, "node_modules/@forgerock/storage": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@forgerock/storage/-/storage-2.0.0.tgz", - "integrity": "sha512-0/1AL8s1UUGQov93n2i3KRalIIZ6P/KZ2pGgg3Ki3LpjVej80BGZkMp05WfQ8S74XkM81S+VmUdWUILzhLtaTA==", + "resolved": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/storage@3abf436", + "integrity": "sha512-f82rzpoEkGOq9pqbIZY/E8dT6z/AwvmtYHH648SnJuFUoP93Z1bOamJOkk9NKtsSOufOowBm9N5mTR3uOD0hlA==", + "license": "MIT", "dependencies": { - "@forgerock/sdk-types": "2.0.0" + "@forgerock/sdk-types": "https://pkg.pr.new/ForgeRock/ping-javascript-sdk/@forgerock/sdk-types@3abf436" } }, "node_modules/@hapi/bourne": {