diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..fa045f3cd --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +@fortawesome:registry=https://npm.fontawesome.com/ +//npm.fontawesome.com/:_authToken=BB192AC2-6663-4FB6-8C01-F1156629CD1F diff --git a/apps/devtool/public/narval-wordmark-white.png b/apps/devtool/public/narval-wordmark-white.png new file mode 100644 index 000000000..8b907e3d4 Binary files /dev/null and b/apps/devtool/public/narval-wordmark-white.png differ diff --git a/apps/devtool/src/app/api/data-store/init.json b/apps/devtool/src/app/api/data-store/init.json index 84d9afd12..cd9f5e00d 100644 --- a/apps/devtool/src/app/api/data-store/init.json +++ b/apps/devtool/src/app/api/data-store/init.json @@ -5,7 +5,7 @@ "addressBook": [], "credentials": [], "tokens": [], - "userGroupMembers": [], + "userGroupMembers": [{ "userId": "user-id", "groupId": "user-group-id" }], "userGroups": [], "userWallets": [], "users": [], diff --git a/apps/devtool/src/app/components/CodeEditor.tsx b/apps/devtool/src/app/components/CodeEditor.tsx new file mode 100644 index 000000000..53485d59d --- /dev/null +++ b/apps/devtool/src/app/components/CodeEditor.tsx @@ -0,0 +1,320 @@ +'use client' + +import Editor from '@monaco-editor/react' +import { EntityUtil, entityDataSchema, policyDataSchema } from '@narval/policy-engine-shared' +import { Jwk, Payload, SigningAlg, hash, hexToBase64Url, signJwt } from '@narval/signature' +import { signMessage } from '@wagmi/core' +import axios from 'axios' +import Image from 'next/image' +import { useEffect, useRef, useState } from 'react' +import { useLocalStorage } from 'usehooks-ts' +import { useAccount, useConnect, useDisconnect } from 'wagmi' +import NarButton from '../design-system/NarButton' +import NarDialog from '../design-system/NarDialog' +import NarInput from '../design-system/NarInput' +import { config } from '../lib/config' + +const DATA_STORE_URL = 'http://127.0.0.1:4200/api/data-store' +const ENGINE_URL = 'http://127.0.0.1:3010' + +const LOCAL_STORAGE_KEYS = { + engineApiKey: 'narvalEngineApiKey', + engineClientId: 'narvalEngineClientId', + engineClientSecret: 'narvalEngineClientSecret', + engineUrl: 'narvalEngineUrl', + entityDataStoreUrl: 'narvalEntityDataStoreUrl', + policyDataStoreUrl: 'narvalPolicyDataStoreUrl', + entitySignatureUrl: 'narvalEntitySignatureUrl', + policySignatureUrl: 'narvalPolicySignatureUrl' +} + +const CodeEditor = () => { + const account = useAccount() + const { connectors, connect } = useConnect() + const { disconnect } = useDisconnect() + + const [engineApiKey, setEngineApiKey] = useLocalStorage(LOCAL_STORAGE_KEYS.engineApiKey, '') + const [engineClientId, setEngineClientId] = useLocalStorage(LOCAL_STORAGE_KEYS.engineClientId, '') + const [engineClientSecret, setEngineClientSecret] = useLocalStorage(LOCAL_STORAGE_KEYS.engineClientSecret, '') + const [engineUrl, setEngineUrl] = useLocalStorage(LOCAL_STORAGE_KEYS.engineUrl, ENGINE_URL) + const [entityDataStoreUrl, setEntityDataStoreUrl] = useLocalStorage( + LOCAL_STORAGE_KEYS.entityDataStoreUrl, + DATA_STORE_URL + ) + const [entitySignatureUrl, setEntitySignatureUrl] = useLocalStorage( + LOCAL_STORAGE_KEYS.entitySignatureUrl, + DATA_STORE_URL + ) + const [policyDataStoreUrl, setPolicyDataStoreUrl] = useLocalStorage( + LOCAL_STORAGE_KEYS.policyDataStoreUrl, + DATA_STORE_URL + ) + const [policySignatureUrl, setPolicySignatureUrl] = useLocalStorage( + LOCAL_STORAGE_KEYS.policySignatureUrl, + DATA_STORE_URL + ) + + const [data, setData] = useState() + const [jwk, setJwk] = useState() + const [isDialogOpen, setIsDialogOpen] = useState(false) + const [validationErrors, setValidationErrors] = useState([]) + + const editorRef = useRef(null) + const monacoRef = useRef(null) + + useEffect(() => { + if (data) return + + const getData = async () => { + const dataStore = await axios.get('/api/data-store') + const { entity, policy } = dataStore.data + setData(JSON.stringify({ entity: entity.data, policy: policy.data }, null, 2)) + } + + getData() + }, [data]) + + useEffect(() => { + if (!account.address) return + if (jwk) return + + setJwk({ + kty: 'EC', + crv: 'secp256k1', + alg: SigningAlg.ES256K, + kid: account.address + }) + }, [account]) + + const sign = async () => { + if (!data || !jwk) return + + const { entity, policy } = JSON.parse(data) + + const entityValidationResult = entityDataSchema.safeParse({ entity: { data: entity } }) + + if (!entityValidationResult.success) { + setValidationErrors( + entityValidationResult.error.errors.map((error) => `${error.path.join('.')}:${error.message}`) + ) + setIsDialogOpen(true) + return + } + + const policyValidationResult = policyDataSchema.safeParse({ policy: { data: policy } }) + + if (!policyValidationResult.success) { + setValidationErrors( + policyValidationResult.error.errors.map((error) => `${error.path.join('.')}:${error.message}`) + ) + setIsDialogOpen(true) + return + } + + const validation = EntityUtil.validate(entity) + + if (!validation.success) { + setValidationErrors(validation.issues.map((issue) => issue.message)) + setIsDialogOpen(true) + return + } + + const jwtSigner = async (message: string) => { + const jwtSig = await signMessage(config, { message }) + + return hexToBase64Url(jwtSig) + } + + if (!account.address) { + throw new Error('No address connected') + } + + const now = Math.floor(Date.now() / 1000) + + const entityPayload: Payload = { + data: hash(entity), + sub: account.address, + iss: 'https://devtool.narval.xyz', + iat: now + } + + const policyPayload: Payload = { + data: hash(policy), + sub: account.address, + iss: 'https://devtool.narval.xyz', + iat: now + } + const entitySig = await signJwt(entityPayload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner) + const policySig = await signJwt(policyPayload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner) + + await axios.post('/api/data-store', { + entity: { + signature: entitySig, + data: entity + }, + policy: { + signature: policySig, + data: policy + } + }) + + console.log('Data signed and stored!') + + await axios.post(`${engineUrl}/tenants/sync`, null, { + headers: { + 'x-client-id': engineClientId, + 'x-client-secret': engineClientSecret + } + }) + + console.log('Data store synced with engine!') + } + + const onboard = async () => { + const { data: tenant } = await axios.post( + `${engineUrl}/tenants`, + { + ...(engineClientId && { clientId: engineClientId }), + entityDataStore: { + dataUrl: entityDataStoreUrl, + signatureUrl: entitySignatureUrl + }, + policyDataStore: { + dataUrl: policyDataStoreUrl, + signatureUrl: policySignatureUrl + } + }, + { + headers: { + 'x-api-key': engineApiKey + } + } + ) + + setEngineClientId(tenant.clientId) + setEngineClientSecret(tenant.clientSecret) + } + + return ( + <> +
+
+ Narval Logo +
+ {!account.isConnected && ( +
+ {connectors.map((connector) => ( + connect({ connector })} + /> + ))} +
+ )} + {account.isConnected && ( + <> + disconnect()} /> + sign()} /> + + )} +
+
+
+
+
+
Entity Data store config:
+
+
Data URL:
+ +
+
+
Signature URL:
+ +
+
+
+
Policy Data store config:
+
+
Data URL:
+ +
+
+
Signature URL:
+ +
+
+
+
+
Policy Engine config:
+
+
Engine URL:
+ +
+
+
Engine API Key:
+ +
+ {engineClientId && ( +
+
Engine Client ID:
+
{engineClientId}
+
+ )} + {engineClientSecret && ( +
+
Engine Client Secret:
+
{engineClientSecret}
+
+ )} + {engineUrl && engineApiKey && !engineClientId && } +
+
+
+ setData(value)} + onMount={(editor, monaco) => { + editorRef.current = editor + monacoRef.current = monaco + }} + /> +
+
+ {isDialogOpen && ( + } + title="Data validation failed" + primaryButtonLabel="OK" + isOpen={isDialogOpen} + onOpenChange={setIsDialogOpen} + onDismiss={() => setIsDialogOpen(false)} + isConfirm + > +
+
    + {validationErrors.map((error, index) => ( +
  • {error}
  • + ))} +
+
+
+ )} + + ) +} + +export default CodeEditor diff --git a/apps/devtool/src/app/components/EditorComponent.tsx b/apps/devtool/src/app/components/EditorComponent.tsx deleted file mode 100644 index cb18d071e..000000000 --- a/apps/devtool/src/app/components/EditorComponent.tsx +++ /dev/null @@ -1,179 +0,0 @@ -'use client' - -import Editor from '@monaco-editor/react' -import { EntityUtil, entityDataSchema, policyDataSchema } from '@narval/policy-engine-shared' -import { Jwk, Payload, SigningAlg, hash, hexToBase64Url, signJwt } from '@narval/signature' -import { getAccount, signMessage } from '@wagmi/core' -import axios from 'axios' -import Image from 'next/image' -import { useEffect, useRef, useState } from 'react' -import { useAccount, useConnect, useDisconnect } from 'wagmi' -import { config } from './config' - -const EditorComponent = () => { - const account = useAccount() - const { connectors, connect } = useConnect() - const { disconnect } = useDisconnect() - - const [data, setData] = useState() - const [displayLink, setDisplayLink] = useState(false) - - const editorRef = useRef(null) - const monacoRef = useRef(null) - - useEffect(() => { - if (data) return - - const getData = async () => { - const dataStore = await axios.get('/api/data-store') - const { entity, policy } = dataStore.data - setData(JSON.stringify({ entity: entity.data, policy: policy.data }, null, 2)) - } - - getData() - }, [data]) - - const sign = async () => { - if (!data) return - - const { entity, policy } = JSON.parse(data) - - const validation = EntityUtil.validate(entity) - - if (!validation.success) { - console.log(validation.issues) - } - - const entityValidationResult = entityDataSchema.safeParse({ entity: { data: entity } }) - const policyValidationResult = policyDataSchema.safeParse({ policy: { data: policy } }) - - if (!entityValidationResult.success) { - console.log('Invalid entity', entityValidationResult.error.errors) - } - - if (!policyValidationResult.success) { - console.log('Invalid policy', policyValidationResult.error.errors) - } - - const jwtSigner = async (msg: string) => { - const jwtSig = await signMessage(config, { message: msg }) - - return hexToBase64Url(jwtSig) - } - - const address = getAccount(config).address - if (!address) throw new Error('No address connected') - - // Need real Jwk - const jwk: Jwk = { - kty: 'EC', - crv: 'secp256k1', - alg: SigningAlg.ES256K, - kid: address - } - - const now = Math.floor(Date.now() / 1000) - - const entityPayload: Payload = { - data: hash(entity), - sub: address, - iss: 'https://devtool.narval.xyz', - iat: now - } - - const policyPayload: Payload = { - data: hash(policy), - sub: address, - iss: 'https://devtool.narval.xyz', - iat: now - } - const entitySig = await signJwt(entityPayload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner) - const policySig = await signJwt(policyPayload, jwk, { alg: SigningAlg.EIP191 }, jwtSigner) - - await axios.post('/api/data-store', { - entity: { - signature: entitySig, - data: entity - }, - policy: { - signature: policySig, - data: policy - } - }) - - setDisplayLink(true) - } - - return ( -
-
- Narval Logo -
- {!account.isConnected && ( -
- {connectors.map((connector) => ( - - ))} -
- )} - {account.isConnected && ( - <> - - - - )} -
-
-
- {displayLink && ( - - Data Store - - )} -
-
- setData(value)} - onMount={(editor, monaco) => { - editorRef.current = editor - monacoRef.current = monaco - }} - /> -
-
- ) -} - -export default EditorComponent diff --git a/apps/devtool/src/app/components/Home.tsx b/apps/devtool/src/app/components/Home.tsx index b94d99871..761e33457 100644 --- a/apps/devtool/src/app/components/Home.tsx +++ b/apps/devtool/src/app/components/Home.tsx @@ -2,15 +2,15 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { WagmiProvider } from 'wagmi' -import EditorComponent from './EditorComponent' -import { config } from './config' +import { config } from '../lib/config' +import CodeEditor from './CodeEditor' const queryClient = new QueryClient() const Home = () => ( - + ) diff --git a/apps/devtool/src/app/design-system/NarButton.tsx b/apps/devtool/src/app/design-system/NarButton.tsx new file mode 100644 index 000000000..30d521171 --- /dev/null +++ b/apps/devtool/src/app/design-system/NarButton.tsx @@ -0,0 +1,107 @@ +import { BaseSyntheticEvent, ForwardedRef, forwardRef, ReactElement, ReactNode } from 'react' +import { classNames } from '../lib/utils' + +interface ButtonProps { + label?: string + type?: 'button' | 'reset' | 'submit' + variant?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'danger' | 'danger-stroke' + size?: 'xs' | 'sm' | 'md' | 'lg' + className?: string + rounded?: boolean + disabled?: boolean + leftIcon?: ReactElement + rightIcon?: ReactElement + children?: ReactNode + onClick?: (e: BaseSyntheticEvent) => void +} + +const getVariantClass = (variant: ButtonProps['variant']) => { + const defaultVariant = 'text-nv-black bg-nv-white hover:bg-nv-neutrals-25' + + switch (variant) { + case 'primary': + return defaultVariant + case 'secondary': + return 'text-nv-white bg-nv-neutrals-800 border border-nv-white' + case 'tertiary': + return 'text-nv-white bg-nv-neutrals-600 border border-nv-neutrals-500 hover:bg-nv-neutrals-400 hover:border-nv-neutrals-400' + case 'quaternary': + return 'text-nv-white !p-0 hover:underline underline-offset-4 focus:!ring-0 focus-visible:!outline-none' + case 'danger': + return 'text-nv-white bg-nv-danger' + case 'danger-stroke': + return 'text-nv-danger bg-nv-neutrals-800 border border-nv-danger hover:border-nv-red-400' + default: + return defaultVariant + } +} + +const getSizeClass = (size: ButtonProps['size']) => { + const defaultSize = 'text-[14px] h-[40px] px-[20px] gap-[8px]' + + switch (size) { + case 'xs': + return 'text-[10px] h-[26px] px-[16px] gap-[2px]' + case 'sm': + return 'text-[12px] h-[32px] px-[16px] gap-[4px]' + case 'md': + return defaultSize + case 'lg': + return 'text-[16px] h-[40px] px-[20px] gap-[8px]' + default: + return defaultSize + } +} + +const btnStyles = 'relative flex items-center justify-center focus:ring focus:ring-nv-brand-500/20' + +const iconStyles = 'flex flex-col items-center justify-center h-[16px] w-[16px]' + +const NarButton = forwardRef( + ( + { + label, + type, + variant = 'primary', + size = 'md', + className, + rounded, + disabled, + leftIcon, + rightIcon, + children, + onClick, + ...props + }: ButtonProps, + forwardedRef: ForwardedRef + ) => { + const btnRounded = rounded ? 'rounded-full' : 'rounded-[12px]' + const btnDisabled = disabled + ? '!text-nv-neutrals-200 hover:!text-nv-neutrals-200 !bg-nv-neutrals-400 hover:!bg-nv-neutrals-400 !border-none cursor-not-allowed' + : '' + + return ( + + ) + } +) + +export default NarButton diff --git a/apps/devtool/src/app/design-system/NarDialog.tsx b/apps/devtool/src/app/design-system/NarDialog.tsx new file mode 100644 index 000000000..f6d784ac9 --- /dev/null +++ b/apps/devtool/src/app/design-system/NarDialog.tsx @@ -0,0 +1,78 @@ +import { faSpinner, faXmark } from '@fortawesome/pro-regular-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import * as Dialog from '@radix-ui/react-dialog' +import { FC, ReactNode } from 'react' +import { classNames } from '../lib/utils' +import NarButton from './NarButton' + +interface NarDialogProps { + title: string + triggerButton: ReactNode + children: ReactNode + isOpen: boolean + isSaving?: boolean + isSaveDisabled?: boolean + isConfirm?: boolean + primaryButtonLabel?: string + onDismiss?: () => void + onSave?: () => void + onOpenChange: (value: boolean) => void +} + +const NarDialog: FC = ({ + title, + triggerButton, + children, + isOpen, + isSaving, + isSaveDisabled, + isConfirm, + primaryButtonLabel, + onDismiss, + onSave, + onOpenChange +}) => ( + + {triggerButton} + + + + + {title} + + {children} + +
+ {!isConfirm && ( + <> + + : undefined} + onClick={onSave} + disabled={isSaving || isSaveDisabled} + /> + + )} + {isConfirm && } +
+ + +
+ +
+
+
+
+
+) + +export default NarDialog diff --git a/apps/devtool/src/app/design-system/NarInput.tsx b/apps/devtool/src/app/design-system/NarInput.tsx new file mode 100644 index 000000000..3e776207a --- /dev/null +++ b/apps/devtool/src/app/design-system/NarInput.tsx @@ -0,0 +1,74 @@ +import { IconDefinition } from '@fortawesome/pro-regular-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { FC, KeyboardEvent, useState } from 'react' +import { classNames } from '../lib/utils' + +interface NarInputProps { + id?: string + label?: string + value?: string + placeholder?: string + className?: string + errorMessage?: string + disabled?: boolean + leftIcon?: IconDefinition + validate?: (value: string | undefined) => boolean + onKeyDown?: (e: KeyboardEvent) => void + onChange: (value: string) => void +} + +const NarInput: FC = ({ + id, + value, + label, + placeholder, + className, + errorMessage, + disabled, + leftIcon, + validate, + onChange, + onKeyDown +}) => { + const [isDirty, setIsDirty] = useState(false) + const [isError, setIsError] = useState(false) + + return ( +
+ {label && ( + + )} +
+ {leftIcon && } + { + if (!isDirty) setIsDirty(true) + if (isError) setIsError(false) + onChange(e.target.value) + }} + onBlur={() => { + if (isDirty && validate && !validate(value)) setIsError(true) + else setIsError(false) + }} + onKeyDown={onKeyDown} + disabled={disabled} + /> +
+ {isError &&
{errorMessage}
} +
+ ) +} + +export default NarInput diff --git a/apps/devtool/src/app/global.css b/apps/devtool/src/app/global.css index b5c61c956..c828f1019 100644 --- a/apps/devtool/src/app/global.css +++ b/apps/devtool/src/app/global.css @@ -1,3 +1,34 @@ @tailwind base; @tailwind components; @tailwind utilities; +@tailwind forms; + +html, +body { + padding: 0; + margin: 0; + font-family: + -apple-system, + BlinkMacSystemFont, + Segoe UI, + Roboto, + Oxygen, + Ubuntu, + Cantarell, + Fira Sans, + Droid Sans, + Helvetica Neue, + sans-serif; + background-color: #08090a; + color: #ffffff; + height: 100%; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} diff --git a/apps/devtool/src/app/components/config.ts b/apps/devtool/src/app/lib/config.ts similarity index 100% rename from apps/devtool/src/app/components/config.ts rename to apps/devtool/src/app/lib/config.ts diff --git a/apps/devtool/src/app/lib/utils.ts b/apps/devtool/src/app/lib/utils.ts new file mode 100644 index 000000000..d24130e97 --- /dev/null +++ b/apps/devtool/src/app/lib/utils.ts @@ -0,0 +1,6 @@ +import { extendTailwindMerge } from 'tailwind-merge' + +export const classNames = (...classes: Array) => { + const twMerge = extendTailwindMerge({ prefix: 'nv-' }) + return twMerge(...classes) +} diff --git a/apps/devtool/src/app/page.tsx b/apps/devtool/src/app/page.tsx index e10f58511..e002ebfbe 100644 --- a/apps/devtool/src/app/page.tsx +++ b/apps/devtool/src/app/page.tsx @@ -3,7 +3,7 @@ import './global.css' export default async function Index() { return ( -
+
) diff --git a/apps/devtool/tailwind.config.js b/apps/devtool/tailwind.config.js index 9a9408662..054c74041 100644 --- a/apps/devtool/tailwind.config.js +++ b/apps/devtool/tailwind.config.js @@ -2,7 +2,225 @@ module.exports = { content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'], theme: { - extend: {} + extend: { + boxShadow: { + nar: '4px 4px 4px rgba(0, 0, 0, 0.1)' + }, + colors: { + 'icon-gray-light': '#A8B0B9', + 'icon-gray-dark': '#71717a', + 'nar-blue': '#0A6BFF', + 'nar-blue-light': '#B9D5FF', + 'nar-blue-lighter': '#458FFF', + 'nar-blue-dark': '#0D063A', + 'nar-cyan': '#01CFFC', + 'nar-gray': '#585C63', + 'nar-gray-dark': '#07031B', + 'nar-gray-light': '#B8B9BD', + 'nar-guild-blue': '#0D0D54', + 'nar-blue-1': '#0C1523', + 'nar-gray-1': '#3F3F3F', + 'nar-gray-2': '#505050', + 'nar-gray-3': 'rgb(47, 48, 55)', + 'nar-blue-gray': '#24242C', + 'nar-bg-dark': '#121621', + 'nar-green': '#08B82F', + 'nar-red': '#B85208', + // New Theme Design + 'nv-black': '#08090A', + 'nv-white': '#FFFFFF', + 'nv-brand': { + 400: '#66B2FF', + 500: '#2E96FF' + }, + 'nv-accent': { + 400: '#5E41F7', + 500: '#582EFF' + }, + 'nv-danger': '#E8303D', + 'nv-warning': '#F9C50B', + 'nv-success': '#25C06D', + 'nv-neutrals': { + 25: '#DDE0E3', + 50: '#A4ABB2', + 100: '#858F98', + 200: '#656E76', + 300: '#434B54', + 400: '#2D3339 ', + 500: '#24292E', + 600: '#202428', + 700: '#1A1D21', + 800: '#14161A', + 900: '#101214' + }, + 'nv-red': { + 100: '#E8C5C7', + 200: '#F0989E', + 300: '#EF7079', + 400: '#F3545F', + 500: '#E8303D', + 600: '#C02833', + 700: '#881A22', + 800: '#5E1318', + 900: '#38090C' + }, + 'nv-orange': { + 100: '#FFEEDF', + 200: '#FFDFC2', + 300: '#FFC998', + 400: '#FFB26A', + 500: '#FF9430', + 600: '#DA802C', + 700: '#B06118', + 800: '#894608', + 900: '#341901' + }, + 'nv-yellow': { + 100: '#FEF9E6', + 200: '#FEF3CD', + 300: '#FDE79B', + 400: '#FBDB6A', + 500: '#F9C50B', + 600: '#CAA10C', + 700: '#9B7C0D', + 800: '#55460F', + 900: '#26210F' + }, + 'nv-green': { + 100: '#EAFBF2', + 200: '#D4F7E4', + 300: '#A9EEC9', + 400: '#7FE6AF', + 500: '#25C06D', + 600: '#219D5A', + 700: '#1C7948', + 800: '#16442C', + 900: '#112119' + }, + 'nv-blue': { + 100: '#E5F2FF', + 200: '#CCE5FF', + 300: '#99CCFF', + 400: '#66B2FF', + 500: '#2E96FF', + 600: '#3883CF', + 700: '#2E669F', + 800: '#093561', + 900: '#141E28' + }, + 'nv-purple': { + 100: '#E9E3FF', + 200: '#C7B9FF', + 300: '#9F87FF', + 400: '#6D49FF', + 500: '#582EFF', + 600: '#4122BC', + 700: '#321996', + 800: '#211162', + 900: '#120C28' + }, + 'nv-pink': { + 100: '#FFE7FF', + 200: '#FECEFF', + 300: '#FEACFF', + 400: '#FD82FF', + 500: '#FC5EFF', + 600: '#D951DB', + 700: '#A83EAA', + 800: '#5A205B', + 900: '#230D23' + } + }, + fontSize: { + xxxs: '.5rem', + xxs: '.65rem', + 'nv-3xs': ['10px', { lineHeight: '10px', letterSpacing: '0.02em' }], + 'nv-2xs': ['12px', { lineHeight: '16px', letterSpacing: '0.02em' }], + 'nv-xs': ['14px', { lineHeight: '20px', letterSpacing: '0.02em' }], + 'nv-sm': ['16px', { lineHeight: '24px', letterSpacing: '0.02em' }], + 'nv-md': ['18px', { lineHeight: '24px', letterSpacing: '0.02em' }], + 'nv-lg': ['20px', { lineHeight: '32px', letterSpacing: '0.02em' }], + 'nv-xl': ['24px', { lineHeight: '32px', letterSpacing: '0.02em' }], + 'nv-2xl': ['28px', { lineHeight: '36px', letterSpacing: '0.02em' }], + 'nv-3xl': ['32px', { lineHeight: '48px', letterSpacing: '0.02em' }], + 'nv-4xl': ['64px', { lineHeight: '80px', letterSpacing: '0.02em' }] + }, + screens: { + sm: '640px', + // => @media (min-width: 640px) { ... } + + md: '768px', + // => @media (min-width: 768px) { ... } + + lg: '1024px', + // => @media (min-width: 1024px) { ... } + + xl: '1280px', + // => @media (min-width: 1280px) { ... } + + '2xl': '1536px', + // => @media (min-width: 1536px) { ... } + + '16in': '1800px' // Bigger than a 16" macbook pro screen + // => @media (min-width: 1800px) { ... } + }, + animation: { + 'spin-slow': 'spin 3s linear infinite', + hide: 'hide 100ms ease-in', + slideIn: 'slideIn 150ms cubic-bezier(0.16, 1, 0.3, 1)', + swipeOut: 'swipeOut 100ms ease-out', + slideDownAndFade: 'slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + slideLeftAndFade: 'slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + slideUpAndFade: 'slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + slideRightAndFade: 'slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)', + overlayShow: 'overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1)', + contentShow: 'contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1)', + bgBlueFlashing: 'bgBlueFlashing 1s infinite' + }, + keyframes: { + bgBlueFlashing: { + '0%': { backgroundColor: '#2E96FF' }, + '50%': { backgroundColor: '#2E669F' }, + '100%': { backgroundColor: '#2E96FF' } + }, + hide: { + from: { opacity: 1 }, + to: { opacity: 0 } + }, + slideIn: { + from: { transform: 'translateX(calc(100% + var(--viewport-padding)))' }, + to: { transform: 'translateX(0)' } + }, + swipeOut: { + from: { transform: 'translateX(var(--radix-toast-swipe-end-x))' }, + to: { transform: 'translateX(calc(100% + var(--viewport-padding)))' } + }, + slideDownAndFade: { + from: { opacity: 0, transform: 'translateY(-2px)' }, + to: { opacity: 1, transform: 'translateY(0)' } + }, + slideLeftAndFade: { + from: { opacity: 0, transform: 'translateX(2px)' }, + to: { opacity: 1, transform: 'translateX(0)' } + }, + slideUpAndFade: { + from: { opacity: 0, transform: 'translateY(2px)' }, + to: { opacity: 1, transform: 'translateY(0)' } + }, + slideRightAndFade: { + from: { opacity: 0, transform: 'translateX(-2px)' }, + to: { opacity: 1, transform: 'translateX(0)' } + }, + overlayShow: { + from: { opacity: 0 }, + to: { opacity: 1 } + }, + contentShow: { + from: { opacity: 0, transform: 'translate(-50%, -48%) scale(0.96)' }, + to: { opacity: 1, transform: 'translate(-50%, -50%) scale(1)' } + } + } + } }, plugins: [] } diff --git a/apps/policy-engine/src/engine/core/service/bootstrap.service.ts b/apps/policy-engine/src/engine/core/service/bootstrap.service.ts index d412aeb71..f390620b1 100644 --- a/apps/policy-engine/src/engine/core/service/bootstrap.service.ts +++ b/apps/policy-engine/src/engine/core/service/bootstrap.service.ts @@ -1,7 +1,5 @@ import { EncryptionService } from '@narval/encryption-module' -import { FIXTURE } from '@narval/policy-engine-shared' import { Injectable, Logger } from '@nestjs/common' -import { randomBytes } from 'crypto' import { TenantService } from './tenant.service' @Injectable() @@ -18,27 +16,6 @@ export class BootstrapService { await this.checkEncryptionConfiguration() - if (!(await this.tenantService.findByClientId(FIXTURE.ORGANIZATION.id))) { - await this.tenantService.onboard({ - clientId: FIXTURE.ORGANIZATION.id, - clientSecret: randomBytes(42).toString('hex'), - dataStore: { - entity: { - dataUrl: 'http://127.0.0.1:3001/storage/2/entity', - signatureUrl: 'http://127.0.0.1:3001/storage/2/entity', - keys: [] - }, - policy: { - dataUrl: 'http://127.0.0.1:3001/storage/2/policy', - signatureUrl: 'http://127.0.0.1:3001/storage/2/policy', - keys: [] - } - }, - createdAt: new Date(), - updatedAt: new Date() - }) - } - await this.syncTenants() } diff --git a/apps/policy-engine/src/engine/http/rest/dto/create-tenant.dto.ts b/apps/policy-engine/src/engine/http/rest/dto/create-tenant.dto.ts index e674b473c..034e79dfc 100644 --- a/apps/policy-engine/src/engine/http/rest/dto/create-tenant.dto.ts +++ b/apps/policy-engine/src/engine/http/rest/dto/create-tenant.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' import { Type } from 'class-transformer' -import { IsDefined, IsString } from 'class-validator' +import { IsDefined, IsOptional, IsString } from 'class-validator' class DataStoreConfigurationDto { dataUrl: string @@ -9,6 +9,7 @@ class DataStoreConfigurationDto { export class CreateTenantDto { @IsString() + @IsOptional() @ApiPropertyOptional() clientId?: string diff --git a/apps/policy-engine/src/main.ts b/apps/policy-engine/src/main.ts index 0b2e8d420..f0c59feff 100644 --- a/apps/policy-engine/src/main.ts +++ b/apps/policy-engine/src/main.ts @@ -58,6 +58,9 @@ const withGlobalFilters = async function bootstrap() { const logger = new Logger('PolicyEngineBootstrap') const application = await NestFactory.create(PolicyEngineModule, { bodyParser: true }) + application.enableCors({ + origin: ['http://localhost:4200'] + }) const configService = application.get(ConfigService) const port = configService.get('port') diff --git a/package-lock.json b/package-lock.json index f53cc3cc0..ff2162b3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,8 @@ "@bull-board/nestjs": "^5.11.0", "@docusaurus/core": "3.1.1", "@docusaurus/preset-classic": "3.1.0", + "@fortawesome/pro-regular-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", "@mdx-js/react": "^3.0.0", "@monaco-editor/react": "^4.6.0", "@nestjs/axios": "^3.0.1", @@ -30,6 +32,7 @@ "@noble/curves": "1.3.0", "@open-policy-agent/opa-wasm": "^1.8.0", "@prisma/client": "^5.7.1", + "@radix-ui/react-dialog": "^1.0.5", "@tanstack/react-query": "^5.24.1", "axios": "^1.6.7", "bull": "^4.12.1", @@ -50,8 +53,10 @@ "reflect-metadata": "^0.1.13", "remeda": "^1.40.0", "rxjs": "^7.8.0", + "tailwind-merge": "^2.2.2", "tslib": "^2.3.0", "type-fest": "^4.10.1", + "usehooks-ts": "^3.0.2", "uuid": "^9.0.1", "viem": "^2.7.16", "wagmi": "^2.5.7", @@ -92,7 +97,7 @@ "@types/uuid": "^9.0.7", "@typescript-eslint/eslint-plugin": "^6.18.0", "@typescript-eslint/parser": "^6.19.1", - "autoprefixer": "^10.4.18", + "autoprefixer": "^10.4.19", "babel-jest": "^29.4.1", "dotenv-cli": "^7.3.0", "eslint": "~8.56.0", @@ -112,7 +117,7 @@ "lint-staged": "15.2.0", "nock": "^13.5.3", "nx": "17.1.3", - "postcss": "^8.4.35", + "postcss": "^8.4.38", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "prisma": "^5.8.1", @@ -3526,9 +3531,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", - "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", + "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -6783,6 +6788,51 @@ "npm": ">=6.14.13" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", + "hasInstallScript": true, + "peer": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/pro-regular-svg-icons": { + "version": "6.5.1", + "resolved": "https://npm.fontawesome.com/@fortawesome/pro-regular-svg-icons/-/6.5.1/pro-regular-svg-icons-6.5.1.tgz", + "integrity": "sha512-Rd7Q7Vff68l/YtFKB4EJW/YJ5eXYk7TqejyDTapOI1sn3/AFMEc8CVCOsPfi4+6qGrJy16hlNvgpigIy5gkW7A==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@golevelup/nestjs-discovery": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@golevelup/nestjs-discovery/-/nestjs-discovery-4.0.0.tgz", @@ -13090,6 +13140,329 @@ "@prisma/debug": "5.8.1" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@react-native-async-storage/async-storage": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.22.3.tgz", @@ -16056,7 +16429,7 @@ "version": "18.2.14", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -17873,6 +18246,17 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", @@ -18182,9 +18566,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "funding": [ { "type": "opencollective", @@ -18201,7 +18585,7 @@ ], "dependencies": { "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "caniuse-lite": "^1.0.30001599", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -19056,9 +19440,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001591", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", - "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", + "version": "1.0.30001600", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", + "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", "funding": [ { "type": "opencollective", @@ -21662,6 +22046,11 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, "node_modules/detect-port": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", @@ -24643,6 +25032,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/get-own-enumerable-property-symbols": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", @@ -33618,9 +34015,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -33638,7 +34035,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -35440,6 +35837,51 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", @@ -35519,6 +35961,28 @@ "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -37092,9 +37556,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "engines": { "node": ">=0.10.0" } @@ -37950,6 +38414,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/tailwind-merge": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", + "integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==", + "dependencies": { + "@babel/runtime": "^7.24.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", @@ -39502,6 +39978,47 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -39510,6 +40027,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.0.2.tgz", + "integrity": "sha512-qJScCj8YOxa8RV3Iz2T+2IsydLG0EID5FouTGE7aNFEpFlCXmRrnJiPCESDArKr1FLTaUQSfDQ43UDn7yMLExw==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", diff --git a/package.json b/package.json index bc98f450e..29b27e249 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@types/uuid": "^9.0.7", "@typescript-eslint/eslint-plugin": "^6.18.0", "@typescript-eslint/parser": "^6.19.1", - "autoprefixer": "^10.4.18", + "autoprefixer": "^10.4.19", "babel-jest": "^29.4.1", "dotenv-cli": "^7.3.0", "eslint": "~8.56.0", @@ -61,7 +61,7 @@ "lint-staged": "15.2.0", "nock": "^13.5.3", "nx": "17.1.3", - "postcss": "^8.4.35", + "postcss": "^8.4.38", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "prisma": "^5.8.1", @@ -80,6 +80,8 @@ "@bull-board/nestjs": "^5.11.0", "@docusaurus/core": "3.1.1", "@docusaurus/preset-classic": "3.1.0", + "@fortawesome/pro-regular-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", "@mdx-js/react": "^3.0.0", "@monaco-editor/react": "^4.6.0", "@nestjs/axios": "^3.0.1", @@ -92,6 +94,7 @@ "@noble/curves": "1.3.0", "@open-policy-agent/opa-wasm": "^1.8.0", "@prisma/client": "^5.7.1", + "@radix-ui/react-dialog": "^1.0.5", "@tanstack/react-query": "^5.24.1", "axios": "^1.6.7", "bull": "^4.12.1", @@ -112,8 +115,10 @@ "reflect-metadata": "^0.1.13", "remeda": "^1.40.0", "rxjs": "^7.8.0", + "tailwind-merge": "^2.2.2", "tslib": "^2.3.0", "type-fest": "^4.10.1", + "usehooks-ts": "^3.0.2", "uuid": "^9.0.1", "viem": "^2.7.16", "wagmi": "^2.5.7",