diff --git a/.changeset/early-wolves-fix.md b/.changeset/early-wolves-fix.md new file mode 100644 index 00000000..2fa5a631 --- /dev/null +++ b/.changeset/early-wolves-fix.md @@ -0,0 +1,60 @@ +--- +"@spruceid/ssx-core": major +"@spruceid/ssx-gnosis-extension": patch +"@spruceid/ssx-react": patch +"@spruceid/ssx": patch +"@spruceid/ssx-server": patch +"@spruceid/ssx-serverless": patch +--- + +Refactor code to avoid duplication and improve performance. + +## General changes: +- Creates a main `tsconfig` file to be extended by packages; +- Configures `ssx-core`as a new package on the monorepo; +- Removes duplicate `@changesets/cli` dependency. + +## @spruceid/ssx-core creation: +- This package was created to held the types definitions and utils functions to all packages. This reduces the amount of duplicated code, prevent circular dependencies, reduces package sizes (tree-shaking during transpilation), and ensures that changes to the API will need a bump to this new package. +- To unify the packages some interfaces/types names were updated: + - `@spruceid/ssx-sdk`: `SSXConfig` -> `SSXClientConfig`; + - `@spruceid/ssx-sdk`: `SSXProviders` -> `SSXClientProviders`; + - `@spruceid/ssx-sdk`: `SSXSession` -> `SSXClientSession`; + - `@spruceid/ssx-server`: `SSXConfig` -> `SSXServerConfig`; + - `@spruceid/ssx-server`: `SSXProviders` -> `SSXServerProviders`; + +## @spruceid/ssx changes: +- Adds `@spruceid/ssx-core` as a dependency; +- Removes all types and interfaces declarations. They were moved to `ssx-core`; +- Exports `SSXConfig` (deprecated) and `SSXClientConfig`; +- Exports `SSXProviders` (deprecated) and `SSXClientProviders`; +- Exports `SSXSession` (deprecated) and `SSXClientSession`; +- Removes all utils functions. They were moved to `ssx-core`; +- Optimizes `try/catch` blocks; +- Updates `examples/ssx-test-dapp` to support ENS resolution from `examples/ssx-test-serverless-dynamodb-api`. + +## @spruceid/ssx-react changes: +- Updates `ssxConfig?: SSXConfig;` on `SSXProviderProps` to `ssxConfig?: SSXClientConfig;` (non breaking change). + +## @spruceid/ssx-gnosis-extension changes: +- Adds `@spruceid/ssx-core` as a dependency; +- Adds types from `ssx-core` to all SSX related variables; +- Optimizes `try/catch` blocks. + +## @spruceid/ssx-server changes: +- Adds `@spruceid/ssx-core` as a dependency; +- Removes all types and interfaces declarations. They were moved to `ssx-core`; +- Exports `SSXConfig` (deprecated) and `SSXServerConfig`; +- Exports `SSXProviders` (deprecated) and `SSXServerProviders`; +- Removes all utils functions. They were moved to `ssx-core`; +- Optimizes `try/catch` blocks. + +## @spruceid/ssx-serverless changes: +- Adds `@spruceid/ssx-core` as a dependency; +- Removes some types and interfaces declarations. They were moved to `ssx-core`; +- Removes all utils functions. They were moved to `ssx-core`; +- Optimizes `try/catch` blocks; +- Changes axios version to `"^0.27.2"`; +- Updates `examples/ssx-test-serverless-dynamodb-api` to resolve ENS accorddly with the request params and fixes the `/ssx-login` JSON response. + + diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..c1da59be --- /dev/null +++ b/.eslintrc @@ -0,0 +1,39 @@ +{ + "env": { + "browser": true, + "es2021": true, + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + ], + "ignorePatterns": [ + "examples/", + "packages/create-ssx-dapp/", + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": ["./tsconfig.json", "./packages/*/tsconfig.json"], + }, + "plugins": [ + "@typescript-eslint", + ], + "root": true, + "rules": { + "object-property-newline": "error", + "import/prefer-default-export": "off", + "no-promise-executor-return": "warn", + "no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }], + "no-use-before-define": "warn", + "import/extensions": "off", + "import/no-unresolved": "off", + "max-len": "warn", + "max-classes-per-file": "off", + "no-plusplus": "off", + "no-await-in-loop": "off", + "@typescript-eslint/no-empty-interface": "off", + }, +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..ab06c3b4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "singleQuote": true, + "arrowParens": "avoid", + "bracketSameLine": true, + "printWidth": 80, + "tabWidth": 2, + "useTabs": false, + "endOfLine": "lf" +} \ No newline at end of file diff --git a/examples/ssx-test-dapp/src/App.js b/examples/ssx-test-dapp/src/App.js index 30053adf..63fa501e 100644 --- a/examples/ssx-test-dapp/src/App.js +++ b/examples/ssx-test-dapp/src/App.js @@ -18,7 +18,10 @@ function AccountInfo({ address, session }) { { session?.ens && - (session?.ens.domain || session?.ens.avatarUrl) ? + ( + session?.ens.domain || session?.ens.avatarUrl || + session?.ens.ensName || session?.ens.ensAvatarUrl + ) ?
ENS @@ -26,18 +29,18 @@ function AccountInfo({ address, session }) {
{ - session.ens.avatarUrl ? + session.ens.avatarUrl || session.ens.ensAvatarUrl ? ENS avatar : null } { - session.ens.domain ? + session.ens.domain || session.ens.ensName ? - {session.ens.domain} + {session.ens.domain || session.ens.ensName} : null } diff --git a/examples/ssx-test-serverless-dynamodb-api/src/libs/siwe.ts b/examples/ssx-test-serverless-dynamodb-api/src/libs/siwe.ts index 09b8c934..8eacc287 100644 --- a/examples/ssx-test-serverless-dynamodb-api/src/libs/siwe.ts +++ b/examples/ssx-test-serverless-dynamodb-api/src/libs/siwe.ts @@ -77,10 +77,11 @@ const signIn: ValidatedEventAPIGatewayProxyEvent = async (e event.body.walletAddress, { daoLogin: true, - resolveEnsDomain: true, - resolveEnsAvatar: true, + resolveEnsDomain: (event.body.resolveEns as any).domain ?? false, + resolveEnsAvatar: (event.body.resolveEns as any).avatar ?? false, } ) + .then((response) => formatJSONResponse(200, { ...response })) .catch(error => formatJSONResponse(500, { error })); }; diff --git a/package.json b/package.json index 7496214c..e16998e9 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "clean": "lerna run clean", "ci": "npm install && npm test", "test": "lerna run test", + "lint": "eslint . --ignore-path .gitignore --ext .ts,.tsx,.js", + "lint:fix": "yarn lint --fix", "documentation": "lerna run doc && yarn documentation:reference", "documentation:reference": "node scripts/build-docs.js", "bootstrap": "lerna bootstrap", @@ -27,6 +29,7 @@ "gnosis": "yarn workspace @spruceid/ssx-gnosis-extension", "sdk": "yarn workspace @spruceid/ssx", "react": "yarn workspace @spruceid/ssx-react", + "core": "yarn workspace @spruceid/ssx-core", "server": "yarn workspace @spruceid/ssx-server", "serverless": "yarn workspace @spruceid/ssx-serverless", "test-dapp": "yarn workspace ssx-test-dapp", @@ -38,11 +41,14 @@ "devDependencies": { "@changesets/cli": "^2.25.0", "concurrently": "^7.3.0", + "eslint": "^8.28.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-prettier": "^4.2.1", "lerna": "^5.1.2", "nx": "^14.5.4", "rimraf": "^3.0.2", - "yarn": "^1.22.19", "typescript": "^4.8.4", - "@changesets/cli": "^2.25.0" + "yarn": "^1.22.19" } } diff --git a/packages/ssx-core/.eslintrc b/packages/ssx-core/.eslintrc new file mode 100644 index 00000000..21758ea0 --- /dev/null +++ b/packages/ssx-core/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../.eslintrc" + ] +} diff --git a/packages/ssx-core/README.md b/packages/ssx-core/README.md new file mode 100644 index 00000000..03b52573 --- /dev/null +++ b/packages/ssx-core/README.md @@ -0,0 +1,13 @@ +# SSX-Core + +SSX-Core is a library made to aggregate types and utils to other SSX packages. + +## Installation + +You can add SSX-Core from yarn or npm: + +```bash +npm install @spruceid/ssx-core +# or +yarn add @spruceid/ssx-core +``` diff --git a/packages/ssx-core/api-documenter.json b/packages/ssx-core/api-documenter.json new file mode 100644 index 00000000..4c1e8d36 --- /dev/null +++ b/packages/ssx-core/api-documenter.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-documenter.schema.json", + "outputTarget": "markdown", + "tableOfContents": { + "nonEmptyCategoryNodeNames": ["References", "Interface6"], + "catchAllCategory": "References", + "categorizeByName": true, + "categoryInlineTag": "docCategory" + } + } \ No newline at end of file diff --git a/packages/ssx-core/api-extractor.json b/packages/ssx-core/api-extractor.json new file mode 100644 index 00000000..c70e9a82 --- /dev/null +++ b/packages/ssx-core/api-extractor.json @@ -0,0 +1,392 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + // "projectFolder": "..", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "/dist/index.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we can specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + */ + "bundledPackages": [], + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the + * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. + * + * DEFAULT VALUE: "false" + */ + // "testMode": false, + + /** + * Specifies how API Extractor sorts members of an enum when generating api.json. By default, the output files + * will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify "preserve". + * + * DEFAULT VALUE: "by-name" + */ + // enumMemberOrder?: EnumMemberOrder, + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + "tsconfigFilePath": "/tsconfig.json" + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": true, + + /** + * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce + * a full file path. + * + * The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/". + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: ".api.md" + */ + // "reportFileName": ".api.md", + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + "reportFolder": "/temp/", + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/" + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + // "untrimmedFilePath": "/dist/.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "none" + // "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "none", + // "addToApiReportFile": false + // } + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/ssx-core/package.json b/packages/ssx-core/package.json new file mode 100644 index 00000000..f0d57975 --- /dev/null +++ b/packages/ssx-core/package.json @@ -0,0 +1,51 @@ +{ + "name": "@spruceid/ssx-core", + "version": "1.0.0", + "description": "SSX core library", + "author": "Spruce Systems Inc.", + "license": "Apache-2.0 OR MIT", + "homepage": "https://github.com/spruceid/ssx/packages/ssx-core#readme", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "bugs": { + "url": "https://github.com/spruceid/ssx/issues" + }, + "directories": { + "src": "src" + }, + "scripts": { + "build": "tsc", + "doc": "yarn doc:extractor && yarn doc:documenter", + "doc:extractor": "api-extractor run", + "doc:documenter": "api-documenter generate -i temp -o ../../documentation/reference/ssx-core" + }, + "engines": { + "node": ">=16.13.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/spruceid/ssx.git" + }, + "dependencies": { + "axios": "^0.27.2", + "express": "^4.18.1", + "express-session": "^1.17.3", + "siwe": "^2.1.2" + }, + "devDependencies": { + "@microsoft/api-documenter": "^7.19.4", + "@microsoft/api-extractor": "^7.29.2", + "@typescript-eslint/eslint-plugin": "^5.23.0", + "@typescript-eslint/parser": "^5.23.0", + "eslint": "^8.15.0", + "eslint-config-prettier": "^8.5.0", + "prettier": "^2.6.2", + "ethers": "^5.6.8" + }, + "peerDependencies": { + "ethers": "^5.6.8" + }, + "files": [ + "dist" + ] +} diff --git a/packages/ssx-core/src/client/index.ts b/packages/ssx-core/src/client/index.ts new file mode 100644 index 00000000..fcdac2de --- /dev/null +++ b/packages/ssx-core/src/client/index.ts @@ -0,0 +1 @@ +export * from './types'; \ No newline at end of file diff --git a/packages/ssx-core/src/client/types.ts b/packages/ssx-core/src/client/types.ts new file mode 100644 index 00000000..e5aae508 --- /dev/null +++ b/packages/ssx-core/src/client/types.ts @@ -0,0 +1,131 @@ +/* eslint-disable no-shadow */ +import { ssxSession } from '@spruceid/ssx-sdk-wasm'; +import { AxiosInstance } from 'axios'; +import { ethers } from 'ethers'; +import { SSXEnsData, SSXEnsResolveOptions, SSXRPCProvider } from '../types'; + +/** Core config for SSX. */ +export interface SSXClientConfig { + /** Whether or not daoLogin is enabled. */ + enableDaoLogin?: boolean; + /** Connection to a cryptographic keypair and/or network. */ + providers?: SSXClientProviders; + /** Optional session configuration for the SIWE message. */ + siweConfig?: SiweConfig; + /** Whether or not ENS resolution is enabled. True means resolve all on client. */ + resolveEns?: boolean | SSXEnsConfig; +} + +/** Representation of an active SSXSession. */ +export type SSXClientSession = { + /** User address */ + address: string; + /** User address without delegation */ + walletAddress: string; + chainId: number; + /** Key to identify the session */ + sessionKey: string; + /** The message that can be obtained by SiweMessage.prepareMessage() */ + siwe: string; + /** The signature of the siwe message */ + signature: string; + /** ENS data supported by SSX */ + ens?: SSXEnsData; +}; + +/** The URL of the server running ssx-server. Providing this field enables SIWE server communication */ +export type SSXServerHost = string; + +/** The ssx-powered server configuration settings */ +export type SSXProviderServer = { + host: SSXServerHost; +}; + +/** Web3 provider configuration settings */ +export interface SSXProviderWeb3 { + /** + * window.ethereum for Metamask; + * web3modal.connect() for Web3Modal; + * const signer = useSigner(); const provider = signer.provider; from Wagmi for Rainbowkit + * */ + driver: any; +} + +/** SSX web3 configuration settings */ +export interface SSXClientProviders { + /** Web3 wallet provider */ + web3?: SSXProviderWeb3; + /** JSON RPC provider configurations */ + rpc?: SSXRPCProvider; + /** Optional reference to server running ssx-server. + * Providing this field enables communication with ssx-server */ + server?: SSXProviderServer; +} + + +/** Optional session configuration for the SIWE message. */ +export interface SiweConfig extends Partial { } + +/** Extra SIWE fields. */ +export type ExtraFields = ssxSession.ExtraFields; + +/** Overrides for the session configuration. */ +export type ConfigOverrides = { + siwe?: SiweConfig +}; + +/** ENS options supported by SSX. */ +export interface SSXEnsConfig { + /** Enable the ENS resolution on server instead of on client. */ + resolveOnServer?: boolean; + /** ENS resolution options. True means resolve all. */ + resolve: SSXEnsResolveOptions; +} + +/** Interface to an intermediate SSX state: connected, but not signed-in. */ +export interface ISSXConnected { + /** Instance of SSXSessionBuilder. */ + builder: ssxSession.SSXSessionBuilder; + /** SSXConfig object. */ + config: SSXClientConfig; + /** List of enabled extensions. */ + extensions: SSXExtension[]; + /** Web3 provider. */ + provider: ethers.providers.Web3Provider; + /** Promise that is initialized on construction to run the "afterConnect" methods of extensions. */ + afterConnectHooksPromise: Promise; + /** Method to verify if extension is enabled. */ + isExtensionEnabled: (namespace: string) => boolean; + /** Axios instance. */ + api?: AxiosInstance; + /** Method to apply the "afterConnect" methods and the delegated capabilities of the extensions. */ + applyExtensions: () => Promise; + /** Method to apply the "afterSignIn" methods of the extensions. */ + afterSignIn: (session: SSXClientSession) => Promise; + /** Method to request nonce from server. */ + ssxServerNonce: (params: Record) => Promise; + /** Method to request sign in from server and return session. */ + ssxServerLogin: (session: SSXClientSession) => Promise; + /** Method to request the user to sign in. */ + signIn: () => Promise; + /** Method to request the user to sign out. */ + signOut: (session: SSXClientSession) => Promise +} + +/** Interface for an extension to SSX. */ +export interface SSXExtension { + /** [recap] Capability namespace. */ + namespace?: string, + /** [recap] Default delegated actions in capability namespace. */ + defaultActions?(): Promise, + /** [recap] Delegated actions by target in capability namespace. */ + targetedActions?(): Promise<{ [target: string]: string[] }>, + /** [recap] Extra metadata to help validate the capability. */ + extraFields?(): Promise, + /** Hook to run after SSX has connected to the user's wallet. + * This can return an object literal to override the session configuration before the user + * signs in. */ + afterConnect?(ssx: ISSXConnected): Promise; + /** Hook to run after SSX has signed in. */ + afterSignIn?(session: SSXClientSession): Promise, +} diff --git a/packages/ssx-core/src/index.ts b/packages/ssx-core/src/index.ts new file mode 100644 index 00000000..e5481a44 --- /dev/null +++ b/packages/ssx-core/src/index.ts @@ -0,0 +1,4 @@ +export * from './types'; +export * from './utils'; +export * from './client'; +export * from './server'; \ No newline at end of file diff --git a/packages/ssx-core/src/server/index.ts b/packages/ssx-core/src/server/index.ts new file mode 100644 index 00000000..f0c4eaa5 --- /dev/null +++ b/packages/ssx-core/src/server/index.ts @@ -0,0 +1,2 @@ +export * from './utils'; +export * from './types'; diff --git a/packages/ssx-core/src/server/types.ts b/packages/ssx-core/src/server/types.ts new file mode 100644 index 00000000..a629d241 --- /dev/null +++ b/packages/ssx-core/src/server/types.ts @@ -0,0 +1,91 @@ +import { CookieOptions } from 'express'; +import { SessionOptions, Store } from 'express-session'; +import { SSXRPCProvider } from '../types'; + + +/** Configuration interface for ssx-server */ +export interface SSXServerConfig { + /** A key used for signing cookies coming from the server. Providing this key enables signed cookies. */ + signingKey?: string; + /** Connection to a cryptographic keypair and/or network. */ + providers?: SSXServerProviders; + /** Changes cookie attributes. Determines whether or not server cookies + * require HTTPS and sets the SameSite attribute to 'lax'. Defaults to false */ + useSecureCookies?: boolean; +} + +/** SSX web3 configuration settings. */ +export interface SSXServerProviders { + /** JSON RPC provider configurations. */ + rpc?: SSXRPCProvider; + /** SSX Session Store configuration settings. */ + sessionConfig?: Partial; + /** Metrics service configurations. */ + metrics?: SSXMetricsProvider; +} + +/** SSX Session Store configuration settings */ +export interface SSXSessionStoreConfig { + /** Overrides for [SessionOptions](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a24d35afe48f7fb702e7617b983ddca1904ba36b/types/express-session/index.d.ts#L52) */ + sessionOptions?: Partial; + /** Connector for different stores */ + store?: (session: any) => Store; +} + +/** SSX Redis Session Store Provider settings. */ +export type SSXRedisSessionStoreProvider = { + service: 'redis'; + redisUrl: string; +}; + +/** SSX Express Session Store Provider settings. */ +export type SSXExpressSessionStoreProvider = { + service: 'express'; + config?: SessionOptions; +}; + +/** SSX Metrics Provider settings. */ +export type SSXMetricsProvider = { + service: 'ssx'; + apiKey: string; +}; + +/** Configuration interface for cookies issued by ssx-server */ +export interface SSXCookieOptions extends CookieOptions { + /** Prevents client-side javascript from accessing cookies. Should always be true. */ + httpOnly: true; + /** Whether or not cookies should be sent over https. Recommend true for production. */ + secure: boolean; + /** Prevents Cross Site Request Forgery Attacks by telling the browser to only send + * cookies with request from your site. The lax setting allows GET requests from + * other sites. Recommended true for production. */ + sameSite: boolean | 'lax' | 'strict' | 'none' | undefined; + /** Whether or not cookies should be signed. Recommended true for production. + * Set to true by providing a signing key. If false, cookies can be tampered + * with on the client. */ + signed: boolean; +} + +/** Allowed fields for an SSX Log. */ +export interface SSXLogFields { + /** Unique identifier for the user, formatted as a DID. */ + userId: string; + /** RFC-3339 time of resource generation, defaults to now. */ + timestamp?: string; + /** Type of content being logged. */ + type: SSXEventLogTypes; + /** Any JSON stringifiable structure to be logged. */ + content: string | Record; +} + +/** Available SSX Log Types. */ +export enum SSXEventLogTypes { + /** Login type definition. */ + LOGIN = 'ssx-login', + /** Logout type definition. */ + // LOGOUT = "ssx-logout", + /** Logging type definition. */ + // LOG = "LOG", + /** Event type definition. */ + // EVENT = "event", +} \ No newline at end of file diff --git a/packages/ssx-core/src/server/utils.ts b/packages/ssx-core/src/server/utils.ts new file mode 100644 index 00000000..d1b4ff94 --- /dev/null +++ b/packages/ssx-core/src/server/utils.ts @@ -0,0 +1,42 @@ +import { AxiosInstance } from "axios"; +import { SSXLogFields } from "./types"; + +/** + * Abstracts the fetch API to append correct headers, host and parse + * responses to JSON for POST requests. + * @param api - Axios Instance. + * @param route - Request route. + * @param body - Request body. + * @returns True (success) or false (error). + */ +export const ssxPost = ( + api: AxiosInstance, + route: string, + body: any +): Promise => { + return api + .post(route, typeof body === 'string' ? body : JSON.stringify(body)) + .then((res: any) => res.status === 204) + .catch((e: any) => { + console.error(e); + return false; + }); +}; + +/** + * Registers a new event to the API. + * @param api - Axios Instance. + * @param apiKey - SSX Platform API Key. + * @param data - SSXLogFields to log. + * @returns True (success) or false (error). + */ +export const ssxLog = async ( + api: AxiosInstance, + apiKey: string, + data: SSXLogFields +): Promise => { + if (!data.timestamp) data.timestamp = new Date().toISOString(); + return ( + Boolean(apiKey) && ssxPost(api, '/events', data) + ); +}; \ No newline at end of file diff --git a/packages/ssx-server/src/types.ts b/packages/ssx-core/src/types.ts similarity index 50% rename from packages/ssx-server/src/types.ts rename to packages/ssx-core/src/types.ts index eb3285ac..f83bb1f9 100644 --- a/packages/ssx-server/src/types.ts +++ b/packages/ssx-core/src/types.ts @@ -1,30 +1,7 @@ import { providers } from 'ethers'; import { ConnectionInfo } from 'ethers/lib/utils'; -import { CookieOptions } from 'express'; -import { SessionOptions, Store } from 'express-session'; - -/** Configuration interface for ssx-server */ -export interface SSXServerConfig { - /** A key used for signing cookies coming from the server. Providing this key enables signed cookies. */ - signingKey?: string; - /** Connection to a cryptographic keypair and/or network. */ - providers?: SSXProviders; - /** Changes cookie attributes. Determines whether or not server cookies - * require HTTPS and sets the SameSite attribute to 'lax'. Defaults to false */ - useSecureCookies?: boolean; -} - -/** SSX web3 configuration settings */ -export interface SSXProviders { - /** JSON RPC provider configurations */ - rpc?: SSXRPCProvider; - // TODO(w4ll3): doc - /** */ - sessionConfig?: Partial; - /** Metrics service configurations */ - metrics?: SSXMetricsProvider; -} +/** Supported provider types. */ export type SSXRPCProvider = | SSXGenericProvider | SSXEtherscanProvider @@ -35,7 +12,7 @@ export type SSXRPCProvider = | SSXAnkrProvider | SSXCustomProvider; -/** Enum of supported RPC providers */ +/** Enum of supported EthersJS providers. */ export enum SSXRPCProviders { SSXAlchemyProvider = 'alchemy', SSXAnkrProvider = 'ankr', @@ -46,7 +23,7 @@ export enum SSXRPCProviders { SSXPocketProvider = 'pocket', } -/** Enum of supported networks for Etherscan */ +/** Enum of supported networks for Etherscan. */ export enum SSXEtherscanProviderNetworks { MAINNET = 'homestead', ROPSTEN = 'ropsten', @@ -55,18 +32,18 @@ export enum SSXEtherscanProviderNetworks { KOVAN = 'kovan', } -/** Etherscan provider settings */ +/** Etherscan provider settings. */ export type SSXEtherscanProvider = { service: SSXRPCProviders.SSXEtherscanProvider; apiKey?: string; network?: SSXEtherscanProviderNetworks; }; -/* Type-Guard for SSXEtherScanProvider */ +/* Type-Guard for SSXEtherScanProvider. */ export const isSSXEtherscanProvider = (provider: SSXRPCProvider): provider is SSXEtherscanProvider => provider.service === SSXRPCProviders.SSXEtherscanProvider; -/** Enum of supported networks for Infura */ +/** Enum of supported networks for Infura. */ export enum SSXInfuraProviderNetworks { MAINNET = 'homestead', ROPSTEN = 'ropsten', @@ -81,24 +58,24 @@ export enum SSXInfuraProviderNetworks { ARBITRUM_RINKEBY = 'arbitrum-rinkeby', } -/** Infura provider project settings */ +/** Infura provider project settings. */ export type SSXInfuraProviderProjectSettings = { projectId: string; projectSecret: string; }; -/** Infura provider settings */ +/** Infura provider settings. */ export type SSXInfuraProvider = { service: SSXRPCProviders.SSXInfuraProvider; apiKey: string | SSXInfuraProviderProjectSettings; network?: SSXInfuraProviderNetworks; }; -/* Type-Guard for SSXInfuraProvider */ +/* Type-Guard for SSXInfuraProvider. */ export const isSSXInfuraProvider = (provider: SSXRPCProvider): provider is SSXInfuraProvider => provider.service === SSXRPCProviders.SSXInfuraProvider; -/** Enum of supported networks for Alchemy */ +/** Enum of supported networks for Alchemy. */ export enum SSXAlchemyProviderNetworks { MAINNET = 'homestead', ROPSTEN = 'ropsten', @@ -113,27 +90,27 @@ export enum SSXAlchemyProviderNetworks { ARBITRUM_RINKEBY = 'arbitrum-rinkeby', } -/** Alchemy provider settings */ +/** Alchemy provider settings. */ export type SSXAlchemyProvider = { service: SSXRPCProviders.SSXAlchemyProvider; apiKey?: string; network?: SSXAlchemyProviderNetworks; }; -/* Type-Guard for SSXAlchemyProvider */ +/* Type-Guard for SSXAlchemyProvider. */ export const isSSXAlchemyProvider = (provider: SSXRPCProvider): provider is SSXAlchemyProvider => provider.service === SSXRPCProviders.SSXAlchemyProvider; -/** Cloudflare provider settings */ +/** Cloudflare provider settings. */ export type SSXCloudflareProvider = { service: SSXRPCProviders.SSXCloudflareProvider; }; -/* Type-Guard for SSXCloudflareProvider */ +/* Type-Guard for SSXCloudflareProvider. */ export const isSSXCloudflareProvider = (provider: SSXRPCProvider): provider is SSXCloudflareProvider => provider.service === SSXRPCProviders.SSXCloudflareProvider; -/** Enum of supported networks for Pocket */ +/** Enum of supported networks for Pocket. */ export enum SSXPocketProviderNetworks { MAINNET = 'homestead', ROPSTEN = 'ropsten', @@ -141,47 +118,47 @@ export enum SSXPocketProviderNetworks { GOERLI = 'goerli', } -/** Pocket provider settings */ +/** Pocket provider settings. */ export type SSXPocketProvider = { service: SSXRPCProviders.SSXPocketProvider; apiKey?: string; network?: SSXPocketProviderNetworks; }; -/* Type-Guard for SSXPocketProvider */ +/** Type-Guard for SSXPocketProvider. */ export const isSSXPocketProvider = (provider: SSXRPCProvider): provider is SSXPocketProvider => provider.service === SSXRPCProviders.SSXPocketProvider; -/** Enum of supported networks for Ankr */ +/** Enum of supported networks for Ankr. */ export enum SSXAnkrProviderNetworks { MAINNET = 'homestead', POLYGON = 'matic', ARBITRUM = 'arbitrum', } -/** Ankr provider settings */ +/** Ankr provider settings. */ export type SSXAnkrProvider = { service: SSXRPCProviders.SSXAnkrProvider; apiKey?: string; network?: SSXAnkrProviderNetworks; }; -/* Type-Guard for SSXAnkrProvider */ +/** Type-Guard for SSXAnkrProvider. */ export const isSSXAnkrProvider = (provider: SSXRPCProvider): provider is SSXAnkrProvider => provider.service === SSXRPCProviders.SSXAnkrProvider; -/** Custom provider settings */ +/** Custom provider settings. */ export type SSXCustomProvider = { service: SSXRPCProviders.SSXCustomProvider; url?: string | ConnectionInfo; network?: providers.Networkish; }; -/* Type-Guard for SSXCustomProvider */ +/** Type-Guard for SSXCustomProvider. */ export const isSSXCustomProvider = (provider: SSXRPCProvider): provider is SSXCustomProvider => provider.service === SSXRPCProviders.SSXCustomProvider; -/** Generic provider settings */ +/** Generic provider settings. */ export type SSXGenericProvider = { service: SSXRPCProviders; url?: string | ConnectionInfo; @@ -189,73 +166,6 @@ export type SSXGenericProvider = { apiKey?: string | SSXInfuraProviderProjectSettings; }; -/** SSX Session Store configuration settings */ -export interface SSXSessionStoreConfig { - /** Overrides for [SessionOptions](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a24d35afe48f7fb702e7617b983ddca1904ba36b/types/express-session/index.d.ts#L52) */ - sessionOptions?: Partial; - /** Connector for different stores */ - store?: (session) => Store; -} - -/** SSX Redis Session Store Provider settings */ -export type SSXRedisSessionStoreProvider = { - service: 'redis'; - redisUrl: string; -}; - -/** SSX Express Session Store Provider settings */ -export type SSXExpressSessionStoreProvider = { - service: 'express'; - // TODO(w4ll3): add app type and/or change config - config?: SessionOptions; -}; - -/** SSX Metrics Provider settings */ -export type SSXMetricsProvider = { - service: 'ssx'; - apiKey: string; -}; - -/** Configuration interface for cookies issued by ssx-server */ -export interface SSXCookieOptions extends CookieOptions { - /** Prevents client-side javascript from accessing cookies. Should always be true. */ - httpOnly: true; - /** Whether or not cookies should be sent over https. Recommend true for production. */ - secure: boolean; - /** Prevents Cross Site Request Forgery Attacks by telling the browser to only send - * cookies with request from your site. The lax setting allows GET requests from - * other sites. Recommended true for production. */ - sameSite: boolean | 'lax' | 'strict' | 'none' | undefined; - /** Whether or not cookies should be signed. Recommended true for production. - * Set to true by providing a signing key. If false, cookies can be tampered - * with on the client */ - signed: boolean; -} - -/** Allowed fields for an SSX Log */ -export interface SSXLogFields { - /** Unique identifier for the user, formatted as a DID */ - userId: string; - /** RFC-3339 time of resource generation, defaults to now */ - timestamp?: string; - /** Type of content being logged */ - type: SSXEventLogTypes; - /** Any JSON stringifiable structure to be logged */ - content: string | Record; -} - -/** Available SSX Log Types */ -export enum SSXEventLogTypes { - /** Login type definition */ - LOGIN = 'ssx-login', - /** Logout type definition */ - // LOGOUT = "ssx-logout", - /** Logging type definition */ - // LOG = "LOG", - /** Event type definition */ - // EVENT = "event", -} - /** ENS options supported by SSX. */ export interface SSXEnsResolveOptions { /** Enable ENS name/domain resolution. */ @@ -264,10 +174,10 @@ export interface SSXEnsResolveOptions { avatar?: boolean; } -/** ENS data supported by SSX */ +/** ENS data supported by SSX. */ export interface SSXEnsData { - /** ENS name/domain */ + /** ENS name/domain. */ domain?: string | null, - /** ENS avatar */ + /** ENS avatar. */ avatarUrl?: string | null } \ No newline at end of file diff --git a/packages/ssx-serverless/src/utils.ts b/packages/ssx-core/src/utils.ts similarity index 54% rename from packages/ssx-serverless/src/utils.ts rename to packages/ssx-core/src/utils.ts index 35d9d439..d24be161 100644 --- a/packages/ssx-serverless/src/utils.ts +++ b/packages/ssx-core/src/utils.ts @@ -8,6 +8,7 @@ import { isSSXPocketProvider, SSXAlchemyProviderNetworks, SSXAnkrProviderNetworks, + SSXEnsData, SSXEtherscanProviderNetworks, SSXInfuraProviderNetworks, SSXPocketProviderNetworks, @@ -15,8 +16,9 @@ import { } from './types'; import { ethers, getDefaultProvider } from 'ethers'; -/** - * Returns an ethers provider based on the RPC configuration +/** + * @param rpc - SSXRPCProvider + * @returns an ethers provider based on the RPC configuration. */ export const getProvider = (rpc?: SSXRPCProvider): ethers.providers.BaseProvider => { if(!rpc) { @@ -60,3 +62,53 @@ export const getProvider = (rpc?: SSXRPCProvider): ethers.providers.BaseProvider } return getDefaultProvider(); }; + +/** + * Resolves ENS data supported by SSX. + * @param provider - Ether provider. + * @param address - User address. + * @param address - User address. + * @param resolveEnsOpts - Options to resolve ENS. + * @returns Object containing ENS data. + */ + export const ssxResolveEns = async ( + provider: ethers.providers.BaseProvider, + /* User Address */ + address: string, + resolveEnsOpts: { + /* Enables ENS domain/name resolution */ + domain?: boolean, + /* Enables ENS avatar resolution */ + avatar?: boolean, + } = { + domain: true, + avatar: true + } + ): Promise => { + if (!address) { + throw new Error('Missing address.'); + } + let ens: SSXEnsData = {}; + let promises: Array> = []; + if (resolveEnsOpts?.domain) { + promises.push(provider.lookupAddress(address)) + } + if (resolveEnsOpts?.avatar) { + promises.push(provider.getAvatar(address)) + } + + await Promise.all(promises) + .then(([domain, avatarUrl]) => { + if (!resolveEnsOpts.domain && resolveEnsOpts.avatar) { + [domain, avatarUrl] = [undefined, domain]; + } + if (domain) { + ens['domain'] = domain; + } + if (avatarUrl) { + ens['avatarUrl'] = avatarUrl; + } + }); + + return ens; +}; \ No newline at end of file diff --git a/packages/ssx-core/tsconfig.json b/packages/ssx-core/tsconfig.json new file mode 100644 index 00000000..506bc079 --- /dev/null +++ b/packages/ssx-core/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/", + "dist/**/*" + ], + "compilerOptions": { + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + } +} diff --git a/packages/ssx-gnosis-extension/.eslintrc b/packages/ssx-gnosis-extension/.eslintrc new file mode 100644 index 00000000..77983042 --- /dev/null +++ b/packages/ssx-gnosis-extension/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../.eslintrc", + ], +} diff --git a/packages/ssx-gnosis-extension/.eslintrc.js b/packages/ssx-gnosis-extension/.eslintrc.js deleted file mode 100644 index c2291ee6..00000000 --- a/packages/ssx-gnosis-extension/.eslintrc.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - }, - extends: [ - 'airbnb-base', - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended" - ], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - }, - plugins: [ - '@typescript-eslint', - ], - rules: { - 'import/prefer-default-export': 'off', - }, -}; diff --git a/packages/ssx-gnosis-extension/package.json b/packages/ssx-gnosis-extension/package.json index 37c199e4..b449cfd9 100644 --- a/packages/ssx-gnosis-extension/package.json +++ b/packages/ssx-gnosis-extension/package.json @@ -15,6 +15,9 @@ "build": "tsc", "clean": "rimraf dist" }, + "dependencies": { + "@spruceid/ssx-core": "*" + }, "peerDependencies": { "siwe": "^2.1.2", "ethers": "^5.7.1" diff --git a/packages/ssx-gnosis-extension/src/gnosis.ts b/packages/ssx-gnosis-extension/src/gnosis.ts index e7f8cf3a..2dbdb31b 100644 --- a/packages/ssx-gnosis-extension/src/gnosis.ts +++ b/packages/ssx-gnosis-extension/src/gnosis.ts @@ -9,17 +9,24 @@ import { Contract, Event, providers, utils, } from 'ethers'; +/** Contract Addresses by network. */ const CONTRACT_ADDRESS = { mainnet: '0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446', rinkeby: '0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446', goerli: '0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446', }; +/** Contract definition for SetDelegate and ClearDelegate events. */ const CONTRACT_ABI = [ 'event SetDelegate(address indexed delegator, bytes32 indexed id, address indexed delegate)', 'event ClearDelegate(address indexed delegator, bytes32 indexed id, address indexed delegate)', ]; +/** + * Gets network name based on chainId. + * @param chainId - chain identifier. + * @returns Network name. + */ const getNetworkName = (chainId: number): string => { switch (chainId) { case 1: @@ -33,12 +40,24 @@ const getNetworkName = (chainId: number): string => { } }; + +/** + * Gets contract address. + * @param provider - EthersJS provider. + * @returns Contract address. + */ const getContractAddress = async ( provider: providers.Provider, ): Promise => provider .getNetwork() .then(({ chainId }) => CONTRACT_ADDRESS[getNetworkName(chainId)]); +/** + * Gets Gnosis delegation history events for an address. + * @param address - User address. + * @param provider - EthersJS provider. + * @returns List of delegation blocks. + */ export const getGnosisDelegationHistoryEventsFor = async ( address: string, provider: providers.Provider, @@ -70,6 +89,12 @@ export const getGnosisDelegationHistoryEventsFor = async ( ]).then((e) => e.flat().sort((a, b) => a.blockNumber - b.blockNumber)); }; +/** + * Gets delegators for an address. + * @param address - User address. + * @param provider - EthersJS provider. + * @returns List of delegators for an address. + */ export const gnosisDelegatorsFor = async ( address: string, provider: providers.Provider, @@ -102,6 +127,13 @@ export const gnosisDelegatorsFor = async ( ); }; +/** + * Verifies if address if delegate of delegator. + * @param delegateAddress - Delegate address. + * @param delegator - Delegator address. + * @param provider - EthersJS provider. + * @returns True (if is delegate) or false (on the contrary). + */ export const addressIsDelegateOf = async ( delegateAddress: string, delegator: string, @@ -111,6 +143,14 @@ export const addressIsDelegateOf = async ( return delegators.includes(delegator); }; +/** + * Giving a message, verifies if the address is a delegee. + * @param params - Verify params. + * @param opts - Verify Options. + * @param message - SIWE Message. + * @param _ + * @returns JSON with information about the delegations. + */ export const SiweGnosisVerify = async ( params: VerifyParams, opts: VerifyOpts, diff --git a/packages/ssx-gnosis-extension/src/modal.ts b/packages/ssx-gnosis-extension/src/modal.ts index f7af8e0c..c062c39a 100644 --- a/packages/ssx-gnosis-extension/src/modal.ts +++ b/packages/ssx-gnosis-extension/src/modal.ts @@ -1,18 +1,30 @@ +import { ConfigOverrides, ISSXConnected, SSXExtension } from '@spruceid/ssx-core'; import { providers } from 'ethers'; import { gnosisDelegatorsFor } from './gnosis'; declare global { interface Window { gnosisModal: IGnosisModal; } } + +/** Gnosis Modal Interface */ interface IGnosisModal { + /** Method to close the modal. */ closeModal: () => void; + /** Method to open modal. */ openModal: () => Promise; + /** Method to select delegation option on modal. */ selectOption: (opt: any) => void; + /** Method to connect with selected option. */ connect: () => Promise; } const styles = '.ssx-gnosis-ssx-gnosis-modal--body{font-family:Satoshi;margin:0;background-color:#000}.ssx-gnosis-modal--container{display:flex;justify-content:center;align-items:center;position:fixed;width:100%;height:100%;top:0;visibility:hidden;opacity:0;transition:all 0.3s ease}.ssx-gnosis-modal--container .ssx-gnosis-modal--backdrop{background:rgba(15,15,24,.6);position:fixed;width:100%;height:100%}.ssx-gnosis-modal--container.visible{opacity:1;visibility:visible}.ssx-gnosis-modal--container .ssx-gnosis-modal--content{max-width:100%;width:500px;position:fixed;top:calc((100vh - 550px - 120px)/2);transition:all 0.8s ease;z-index:9999}.ssx-gnosis-modal--header,.ssx-gnosis-modal--subheader,.ssx-gnosis-modal--footer{padding:1rem}.ssx-gnosis-modal--body{height:300px;overflow-y:auto}.ssx-gnosis-modal--body .ssx-gnosis-modal--option{font-size:16px;font-weight:400;padding:15px 30px;cursor:pointer}.ssx-gnosis-modal--body .ssx-gnosis-modal--info{width:100%;height:100%;display:flex;flex-wrap:wrap;text-align:center;justify-content:center;align-items:center}.ssx-gnosis-modal--body .ssx-gnosis-modal--info p{font-size:26px;font-weight:700;line-height:30px;margin-top:22px;margin-bottom:22px}.ssx-gnosis-modal--body .ssx-gnosis-modal--info a{-webkit-appearance:button;-moz-appearance:button;appearance:button;text-decoration:none;cursor:pointer;border-radius:100px;width:174px;height:45px;font-size:16px;font-weight:700;line-height:25px;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;margin:auto}.ssx-gnosis-modal--body .ssx-gnosis-modal--info a img{margin-right:12px}.ssx-gnosis-modal--body .ssx-gnosis-modal--loader{width:100%;height:100%;display:flex;flex-wrap:wrap;justify-content:center;align-items:center}.ssx-gnosis-modal--body .ssx-gnosis-modal--loader img{width:100px;height:100px}.ssx-gnosis-modal--header{display:flex;justify-content:space-between;align-items:center}.ssx-gnosis-modal--header .ssx-gnosis-modal--brand{display:flex;flex-wrap:wrap;align-items:center;justify-content:center}.ssx-gnosis-modal--header h1{margin-left:12px;font-size:30px;line-height:46px;font-weight:900}.ssx-gnosis-modal--header button{font-size:20px;padding:12px;margin:0;height:40px;width:40px;border-radius:100px;border-style:none;cursor:pointer}.ssx-gnosis-modal--subheader{border-top-left-radius:20px;border-top-right-radius:20px;text-align:right}.ssx-gnosis-modal--subheader .ssx-gnosis-modal--results-counter{font-size:16px;font-weight:400;line-height:25.5px}.ssx-gnosis-modal--footer{text-align:right;border-bottom-left-radius:20px;border-bottom-right-radius:20px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap}.ssx-gnosis-modal--btn{display:inline-block;border:0;cursor:pointer;font-size:16px;line-height:24.5px;font-weight:700;padding:11px 25px}.ssx-gnosis-modal--btn.disabled{cursor:not-allowed;opacity:.4}.ssx-gnosis-modal-rotating{animation:ssx-gnosis-modal-rotating 0.7s linear infinite;-o-animation:ssx-gnosis-modal-rotating 0.7s linear infinite;-ms-animation:ssx-gnosis-modal-rotating 0.7s linear infinite;-moz-animation:ssx-gnosis-modal-rotating 0.7s linear infinite;-webkit-animation:ssx-gnosis-modal-rotating 0.7s linear infinite}@keyframes ssx-gnosis-modal-rotating{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}.ssx-gnosis-modal--theme-dark{color:#fff}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--header button{background:#fff;color:#000}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--subheader,.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body{background-color:#293137}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body .ssx-gnosis-modal--info a{background-color:#fff;color:#24262A}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body .ssx-gnosis-modal--info a img{filter:brightness(0) saturate(100%) invert(30%) sepia(96%) saturate(1424%) hue-rotate(147deg) brightness(90%) contrast(101%)}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body .ssx-gnosis-modal--loader img{filter:invert(1)}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body::-webkit-scrollbar-track{border-radius:20px;background-color:#3F464B}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body::-webkit-scrollbar-thumb{border-radius:8px;background-color:#898f94}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body::-webkit-scrollbar{background-color:#898f94;border-radius:20px;height:6px;width:6px}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body .ssx-gnosis-modal--option{background-color:transparent;border-bottom:1px solid #3F464B}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body .ssx-gnosis-modal--option.selected{background-color:#24262A}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--body .ssx-gnosis-modal--option:hover{background-color:#24262A}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--footer{background-color:#3F464B}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--subheader{border-bottom:1px solid #3F464B}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--footer{border-top:1px solid #3F464B}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--btn.secondary{background:transparent;color:#ccc}.ssx-gnosis-modal--theme-dark .ssx-gnosis-modal--btn.primary{border-radius:8px;background:#656B6F;color:#fff}'; + +/** + * Gets modal loader component. + * @returns Modal Loader HTML element. + */ const getModalLoader = (): Element => { const loader = document.createElement("div"); loader.classList.add("ssx-gnosis-modal--loader"); @@ -41,6 +53,10 @@ const getModalLoader = (): Element => { return loader; } +/** + * Gets error component. + * @returns Modal Error HTML element. + */ const getErrorModal = (): Element => { const info = document.createElement('div'); info.classList.add('ssx-gnosis-modal--info'); @@ -108,6 +124,10 @@ const getErrorModal = (): Element => { return info; } +/** + * Gets base modal component. + * @returns Base Modal HTML element. + */ const getBaseModal = (): Element => { const container = document.createElement("div"); container.classList.add("ssx-gnosis-modal--container", "ssx-gnosis-modal--theme-dark"); @@ -204,17 +224,29 @@ const getBaseModal = (): Element => { return container; } - -export class GnosisDelegation { +/** + * GnosisDelegation is an SSX Extension to enable multisig login on SSX. + */ +export class GnosisDelegation implements SSXExtension { + /** Web3 Provider. */ public web3provider: providers.Web3Provider; + /** Selected delegation option. */ public selectedOption: string = ''; - private _proceed: (value: any | PromiseLike) => void; + /** Continue with the SSX flow with a successfull promise. */ + private _proceed: (value: ConfigOverrides | PromiseLike) => void; + /** Continue with the SSX flow with a rejected promise. */ private _failure: (reason?: any) => void; + /** User address without delegation. */ private _connectedAddress: string; namespace = "delegationRegistry"; - async afterConnect(ssx: any): Promise { + /** + * Executes extension logic. + * @param ssx - SSXConnected instance. + * @returns Promise with extension status. + */ + async afterConnect(ssx: ISSXConnected): Promise { this.web3provider = ssx.provider; this._connectedAddress = await ssx.provider.getSigner().getAddress(); @@ -226,14 +258,17 @@ export class GnosisDelegation { }; window.gnosisModal = gnosisModal; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this.openModal(); this._proceed = resolve; this._failure = reject; }); } - openModal = async () => { + /** + * Opens GnosisModal. + */ + openModal = async (): Promise => { this.selectedOption = ''; const { modal, modalBody, modalCounter, continueBtn, @@ -284,6 +319,11 @@ export class GnosisDelegation { }); }; + + /** + * Gets static HTML tags to build the main component. + * @returns JSON with static HTML tags. + */ getTags = (): Record => { // append modal to the body const modalWrapper = document.createElement('div'); @@ -311,22 +351,37 @@ export class GnosisDelegation { }; }; + /** + * Gets delegators. + * @returns List of delegators. + */ getOptions = async (): Promise> => gnosisDelegatorsFor( this._connectedAddress, this.web3provider, ); - selectOption = (option) => { + /** + * Selects delegation option. + * @param option - Modal option. + */ + selectOption = (option): void => { document.getElementById(`ssx-gnosis-modal--option-${this.selectedOption}`).classList.remove('selected'); document.getElementById('ssx-gnosis-modal--continue-btn').classList.remove('disabled'); this.selectedOption = option; document.getElementById(`ssx-gnosis-modal--option-${option}`).classList.add('selected'); }; - closeModal = () => { + /** + * Closes Modal. + */ + closeModal = (): void => { document.getElementById('ssx-gnosis-modal--wrapper').remove(); }; + /** + * Confirm selection and continue SSX flow. + * @returns Promise void. + */ connect = async (): Promise => { if (!this.selectedOption.replace(/Yourself - /, '')) { this._failure(new Error("Invalid address selected.")); diff --git a/packages/ssx-gnosis-extension/tsconfig.json b/packages/ssx-gnosis-extension/tsconfig.json index e0a0c4d9..506bc079 100644 --- a/packages/ssx-gnosis-extension/tsconfig.json +++ b/packages/ssx-gnosis-extension/tsconfig.json @@ -1,103 +1,13 @@ { - "exclude": ["dist/", "node_modules"], + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/", + "dist/**/*" + ], "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "lib": ["es2019"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ } } diff --git a/packages/ssx-react/src/ssx.tsx b/packages/ssx-react/src/ssx.tsx index 81ead924..1ddcd5ac 100644 --- a/packages/ssx-react/src/ssx.tsx +++ b/packages/ssx-react/src/ssx.tsx @@ -1,24 +1,24 @@ import { useContext, createContext, useState, useEffect, ReactNode } from 'react'; -import { SSX, SSXConfig } from "@spruceid/ssx"; +import { SSX, SSXClientConfig } from "@spruceid/ssx"; import { useSigner } from 'wagmi'; -/** Props for SSX Provider */ +/** Props for SSX Provider. */ export interface SSXProviderProps { - /** Optional SSX configuration, used for instantiating an SSX Instance */ - ssxConfig?: SSXConfig; - /** Provider child nodes, for rendering*/ + /** Optional SSX configuration, used for instantiating an SSX Instance. */ + ssxConfig?: SSXClientConfig; + /** Provider child nodes, for rendering. */ children: ReactNode; } -/** Interface for contents provided to the Hook */ +/** Interface for contents provided to the Hook. */ export interface SSXContextInterface { - /** SSX Instance */ + /** SSX Instance. */ ssx: SSX | undefined; - /** SSX Instance loading state */ + /** SSX Instance loading state. */ ssxLoaded: boolean; } -/** Default, uninitialized context */ +/** Default, uninitialized context. */ const defaultContext: SSXContextInterface = { ssx: undefined, ssxLoaded: false, @@ -26,7 +26,7 @@ const defaultContext: SSXContextInterface = { const SSXContext = createContext(defaultContext); -/** SSX Provider Component */ +/** SSX Provider Component. */ export const SSXProvider = ({ ssxConfig, children }: SSXProviderProps) => { const { data: signer, isSuccess: signerLoaded } = (typeof window !== 'undefined' && useSigner()) || { data: undefined, isSuccess: false }; const [ssx, setSSX] = useState(); @@ -66,7 +66,7 @@ export const SSXProvider = ({ ssxConfig, children }: SSXProviderProps) => { ); } -/** Hook for accessing SSX instance and state */ +/** Hook for accessing SSX instance and state. */ export const useSSX = (): SSXContextInterface => { return useContext(SSXContext); }; \ No newline at end of file diff --git a/packages/ssx-react/tsconfig.json b/packages/ssx-react/tsconfig.json index efee40a1..506bc079 100644 --- a/packages/ssx-react/tsconfig.json +++ b/packages/ssx-react/tsconfig.json @@ -1,102 +1,13 @@ { - "include": ["src/**/*"], - "exclude": ["node_modules/", "dist/**/*"], + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/", + "dist/**/*" + ], "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "lib": ["es2019"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ } } diff --git a/packages/ssx-sdk/.eslintrc b/packages/ssx-sdk/.eslintrc new file mode 100644 index 00000000..77983042 --- /dev/null +++ b/packages/ssx-sdk/.eslintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "../../.eslintrc", + ], +} diff --git a/packages/ssx-sdk/.eslintrc.js b/packages/ssx-sdk/.eslintrc.js deleted file mode 100644 index 771e9246..00000000 --- a/packages/ssx-sdk/.eslintrc.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - }, - extends: [ - 'airbnb-base', - ], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - }, - plugins: [ - '@typescript-eslint', - ], - rules: { - 'object-property-newline': 'error', - 'import/prefer-default-export': 'off', - 'no-promise-executor-return': 'warn', - 'no-unused-vars': ['warn', { vars: 'all', args: 'after-used', ignoreRestSiblings: false }], - 'no-use-before-define': 'warn', - 'import/extensions': 'off', - 'import/no-unresolved': 'off', - 'max-len': 'warn', - 'max-classes-per-file': 'off', - 'no-plusplus': 'off', - 'no-await-in-loop': 'off', - }, -}; diff --git a/packages/ssx-sdk/api-extractor.json b/packages/ssx-sdk/api-extractor.json index 83ecb020..0fd4df20 100644 --- a/packages/ssx-sdk/api-extractor.json +++ b/packages/ssx-sdk/api-extractor.json @@ -60,7 +60,7 @@ * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been * local files for library1. */ - "bundledPackages": ["@spruceid/siwe", "@spruceid/ssx-wasm"], + "bundledPackages": ["@spruceid/ssx-core", "@spruceid/ssx-sdk-wasm"], /** * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files diff --git a/packages/ssx-sdk/package.json b/packages/ssx-sdk/package.json index 4160c694..b709fb84 100644 --- a/packages/ssx-sdk/package.json +++ b/packages/ssx-sdk/package.json @@ -27,6 +27,7 @@ "dependencies": { "@metamask/detect-provider": "^1.2.0", "siwe": "^2.1.2", + "@spruceid/ssx-core": "*", "@spruceid/ssx-gnosis-extension": "*", "@spruceid/ssx-sdk-wasm": "0.1.2", "@types/lodash.merge": "^4.6.7", diff --git a/packages/ssx-sdk/src/core.ts b/packages/ssx-sdk/src/core.ts index a798baec..3d4fe1ea 100644 --- a/packages/ssx-sdk/src/core.ts +++ b/packages/ssx-sdk/src/core.ts @@ -6,53 +6,58 @@ import { import merge from 'lodash.merge'; import axios, { AxiosInstance } from 'axios'; import { generateNonce } from 'siwe'; -import { SSXExtension } from './extension'; import { - SSXSession, - SSXConfig, + SSXClientSession, + SSXClientConfig, SSXEnsResolveOptions, -} from './types'; + ISSXConnected, + SSXExtension +} from '@spruceid/ssx-core'; -/** Initializer for an SSXSession. */ +/** Initializer for an SSXClientSession. */ export class SSXInit { - /** Extensions for the SSXSession. */ + /** Extensions for the SSXClientSession. */ private extensions: SSXExtension[] = []; - constructor(private config?: SSXConfig) { } + constructor(private config?: SSXClientConfig) { } /** Extend the session with an SSX compatible extension. */ extend(extension: SSXExtension) { this.extensions.push(extension); } - /** Connect to the signing account using the configured provider. */ + /** + * Connect to the signing account using the configured provider. + * @returns SSXConnected instance. + */ async connect(): Promise { // TODO(w4ll3): consider creating a custom error object, i.e: SSXConnectError let provider: ethers.providers.Web3Provider; - try { - // eslint-disable-next-line no-underscore-dangle - if (!this.config.providers.web3.driver?._isProvider) { - provider = new ethers.providers.Web3Provider(this.config.providers.web3.driver); - } else { - provider = this.config.providers.web3.driver; - } + // eslint-disable-next-line no-underscore-dangle + if (!this.config.providers.web3.driver?._isProvider) { try { - if (!this.config.providers.web3?.driver?.bridge?.includes('walletconnect')) { - const connectedAccounts = await provider.listAccounts(); - if (connectedAccounts.length === 0) { - await provider.send('wallet_requestPermissions', [{ eth_accounts: {} }]); - } - } + provider = new ethers.providers.Web3Provider(this.config.providers.web3.driver); } catch (err) { - // Permission rejected error + // Provider creation error console.error(err); throw err; } - } catch (err) { - // Provider creation error - console.error(err); - throw err; + } else { + provider = this.config.providers.web3.driver; + } + + if (!this.config.providers.web3?.driver?.bridge?.includes('walletconnect')) { + const connectedAccounts = await provider.listAccounts(); + if (connectedAccounts.length === 0) { + try { + await provider.send('wallet_requestPermissions', [{ eth_accounts: {} }]); + } catch (err) { + // Permission rejected error + console.error(err); + throw err; + } + } } let builder; @@ -70,20 +75,27 @@ export class SSXInit { } /** An intermediate SSX state: connected, but not signed-in. */ -export class SSXConnected { - /** Promise that is initialized on construction of this class to run the "afterConnect" methods +export class SSXConnected implements ISSXConnected { + /** + * Promise that is initialized on construction of this class to run the "afterConnect" methods * of the extensions. */ public afterConnectHooksPromise: Promise; + /** Verifies if extension is enabled. */ public isExtensionEnabled = (namespace: string) => this.extensions.filter((e) => e.namespace === namespace).length === 1; + /** Axios instance. */ public api?: AxiosInstance; constructor( + /** Instance of SSXSessionBuilder */ public builder: ssxSession.SSXSessionBuilder, - public config: SSXConfig, + /** SSXConfig object. */ + public config: SSXClientConfig, + /** Enabled extensions. */ public extensions: SSXExtension[], + /** EthersJS provider. */ public provider: ethers.providers.Web3Provider, ) { this.afterConnectHooksPromise = this.applyExtensions(); @@ -96,7 +108,6 @@ export class SSXConnected { } /** Applies the "afterConnect" methods and the delegated capabilities of the extensions. */ - public async applyExtensions(): Promise { for (const extension of this.extensions) { if (extension.afterConnect) { @@ -127,8 +138,11 @@ export class SSXConnected { } } - /** Applies the "afterSignIn" methods of the extensions. */ - public async afterSignIn(session: SSXSession): Promise { + /** + * Applies the "afterSignIn" methods of the extensions. + * @param session - SSXClientSession object. + */ + public async afterSignIn(session: SSXClientSession): Promise { for (const extension of this.extensions) { if (extension.afterSignIn) { await extension.afterSignIn(session); @@ -136,30 +150,39 @@ export class SSXConnected { } } + /** + * Requests nonce from server. + * @param params - Request params. + * @returns Promise with nonce. + */ public async ssxServerNonce(params: Record): Promise { - try { - if (this.api) { - const { data: nonce } = await this.api.get('/ssx-nonce', { params }); - if (!nonce) { - throw new Error('Unable to retrieve nonce from server.'); - } - return nonce; + if (this.api) { + let nonce; + try { + nonce = (await this.api.get('/ssx-nonce', { params })).data; + } catch (error) { + console.error(error); + throw error; } - } catch (error) { - // were do we log this error? ssx.log? - // show to user? - console.error(error); - throw error; + if (!nonce) { + throw new Error('Unable to retrieve nonce from server.'); + } + return nonce; } } - public async ssxServerLogin(session: SSXSession): Promise { - try { - if (this.api) { - let resolveEns: boolean | SSXEnsResolveOptions = false; - if (typeof this.config.resolveEns === 'object' && this.config.resolveEns.resolveOnServer) { - resolveEns = this.config.resolveEns.resolve; - } + /** + * Requests sign in from server and returns session. + * @param session - SSXClientSession object. + * @returns Promise with server session data. + */ + public async ssxServerLogin(session: SSXClientSession): Promise { + if (this.api) { + let resolveEns: boolean | SSXEnsResolveOptions = false; + if (typeof this.config.resolveEns === 'object' && this.config.resolveEns.resolveOnServer) { + resolveEns = this.config.resolveEns.resolve; + } + try { // @TODO(w4ll3): figure out how to send a custom sessionKey return this.api.post('/ssx-login', { signature: session.signature, @@ -171,22 +194,21 @@ export class SSXConnected { resolveEns }) .then((response) => response.data); + } catch (error) { + console.error(error); + throw error; } - } catch (error) { - // were do we log this error? ssx.log? - // show to user? - console.error(error); - throw error; } } - /** Requests the user to sign in. - * - * Generates the SIWE message for this session, requests the configured - * Signer to sign the message, calls the "afterSignIn" methods of the - * extensions and returns the SSXSession object. - */ - async signIn(): Promise { + /** + * Requests the user to sign in. + * Generates the SIWE message for this session, requests the configured + * Signer to sign the message, calls the "afterSignIn" methods of the + * extensions. + * @returns Promise with the SSXClientSession object. + */ + async signIn(): Promise { await this.afterConnectHooksPromise; const sessionKey = this.builder.jwk(); @@ -232,16 +254,18 @@ export class SSXConnected { return session; } - async signOut(session: SSXSession): Promise { - try { - if (this.api) { + /** + * Requests the user to sign out. + * @param session - SSXClientSession object. + */ + async signOut(session: SSXClientSession): Promise { + if (this.api) { + try { await this.api.post('/ssx-logout', { ...session }); + } catch (error) { + console.error(error); + throw error; } - } catch (error) { - // were do we log this error? ssx.log? - // show to user? - console.error(error); - throw error; } } } diff --git a/packages/ssx-sdk/src/extension.ts b/packages/ssx-sdk/src/extension.ts deleted file mode 100644 index 45953565..00000000 --- a/packages/ssx-sdk/src/extension.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ssxSession } from '@spruceid/ssx-sdk-wasm'; -import { SSXConnected } from './core'; -import { SSXSession, SiweConfig } from './types'; - -/** Interface for an extension to SSX. */ -export interface SSXExtension { - /** [recap] Capability namespace. */ - namespace?: string, - /** [recap] Default delegated actions in capability namespace. */ - defaultActions?(): Promise, - /** [recap] Delegated actions by target in capability namespace. */ - targetedActions?(): Promise<{ [target: string]: string[] }>, - /** [recap] Extra metadata to help validate the capability. */ - extraFields?(): Promise, - /** Hook to run after SSX has connected to the user's wallet. - * This can return an object literal to override the session configuration before the user - * signs in. */ - afterConnect?(ssx: SSXConnected): Promise; - /** Hook to run after SSX has signed in. */ - afterSignIn?(session: SSXSession): Promise, -} - -export type ExtraFields = ssxSession.ExtraFields; - -/** Overrides for the session configuration. */ -export type ConfigOverrides = { - siwe?: SiweConfig -}; diff --git a/packages/ssx-sdk/src/index.ts b/packages/ssx-sdk/src/index.ts index e1fa61a3..445bfc77 100644 --- a/packages/ssx-sdk/src/index.ts +++ b/packages/ssx-sdk/src/index.ts @@ -1,4 +1,12 @@ export * from './ssx'; -export * from './extension'; export * from './core'; -export * from './types'; +export * from '@spruceid/ssx-core/dist/client/types'; +export * from '@spruceid/ssx-core/dist/types'; +export { + /** @deprecated use SSXClientConfig field instead */ + SSXClientConfig as SSXConfig, + /** @deprecated use SSXClientProviders field instead */ + SSXClientProviders as SSXProviders, + /** @deprecated use SSXClientSession field instead */ + SSXClientSession as SSXSession, +} from '@spruceid/ssx-core'; \ No newline at end of file diff --git a/packages/ssx-sdk/src/ssx.ts b/packages/ssx-sdk/src/ssx.ts index 88f6e3f9..42005597 100644 --- a/packages/ssx-sdk/src/ssx.ts +++ b/packages/ssx-sdk/src/ssx.ts @@ -4,12 +4,12 @@ import { SSXInit, } from './core'; import { - SSXConfig, - SSXSession, + SSXClientConfig, + SSXClientSession, SSXRPCProviders, SSXEnsData, SSXEnsResolveOptions, -} from './types'; +} from '@spruceid/ssx-core'; declare global { interface Window { @@ -17,7 +17,7 @@ declare global { } } -const SSX_DEFAULT_CONFIG: SSXConfig = { +const SSX_DEFAULT_CONFIG: SSXClientConfig = { providers: { web3: { driver: globalThis.ethereum, @@ -30,11 +30,11 @@ const SSX_DEFAULT_CONFIG: SSXConfig = { * A toolbox for user-controlled identity, credentials, storage and more. */ export class SSX { - /** SSXSession builder. */ + /** SSXClientSession builder. */ private init: SSXInit; /** The session representation (once signed in). */ - public session?: SSXSession; + public session?: SSXClientSession; /** Current connection of SSX */ public connection?: SSXConnected; @@ -42,7 +42,7 @@ export class SSX { /** Supported RPC Providers */ public static RPCProviders = SSXRPCProviders; - constructor(private config: SSXConfig = SSX_DEFAULT_CONFIG) { + constructor(private config: SSXClientConfig = SSX_DEFAULT_CONFIG) { this.init = new SSXInit({ ...this.config, providers: { ...SSX_DEFAULT_CONFIG.providers, ...this.config?.providers } }); if (this.config.enableDaoLogin) { @@ -55,7 +55,7 @@ export class SSX { * Request the user to sign in, and start the session. * @returns Object containing information about the session */ - async signIn(): Promise { + async signIn(): Promise { try { this.connection = await this.init.connect(); } catch (err) { @@ -66,19 +66,19 @@ export class SSX { try { this.session = await this.connection.signIn(); - if (this.config.resolveEns) { - if (this.config.resolveEns === true) { - this.session.ens = await this.resolveEns(this.session.address); - } else if (!this.config.resolveEns.resolveOnServer) { - this.session.ens = await this.resolveEns(this.session.address, this.config.resolveEns.resolve); - } - } - return this.session; } catch (err) { // Request to /ssx-login went wrong console.error(err); throw err; } + if (this.config.resolveEns) { + if (this.config.resolveEns === true) { + this.session.ens = await this.resolveEns(this.session.address); + } else if (!this.config.resolveEns.resolveOnServer) { + this.session.ens = await this.resolveEns(this.session.address, this.config.resolveEns.resolve); + } + } + return this.session; } /** @@ -91,9 +91,9 @@ export class SSX { /** User address */ address: string, resolveEnsOpts: SSXEnsResolveOptions = { - domain: true, - avatar: true - } + domain: true, + avatar: true + } ): Promise { if (!address) { throw new Error('Missing address.'); @@ -126,21 +126,27 @@ export class SSX { /** * Invalidates user's session. */ - async signOut() { + async signOut(): Promise { try { await this.connection.signOut(this.session); - this.session = null; - this.connection = null; } catch (err) { // request to /ssx-logout went wrong console.error(err); throw err; } + this.session = null; + this.connection = null; } - /** Get the address that is connected and signed in. */ + /** + * Gets the address that is connected and signed in. + * @returns Address. + */ address: () => string | undefined = () => this.session?.address; - /** Get the chainId that the address is connected and signed in on. */ + /** + * Get the chainId that the address is connected and signed in on. + * @returns chainId. + */ chainId: () => number | undefined = () => this.session?.chainId; } diff --git a/packages/ssx-sdk/src/types.ts b/packages/ssx-sdk/src/types.ts deleted file mode 100644 index 11b7cbfa..00000000 --- a/packages/ssx-sdk/src/types.ts +++ /dev/null @@ -1,225 +0,0 @@ -/* eslint-disable no-shadow */ -import { ssxSession } from '@spruceid/ssx-sdk-wasm'; -import { providers } from 'ethers'; -import { ConnectionInfo } from 'ethers/lib/utils'; - -/** Supported storage types. */ -export enum StorageType { -} - -/** Core config for SSX. */ -export interface SSXConfig { - /** Whether or not daoLogin is enabled. */ - enableDaoLogin?: boolean; - /** Connection to a cryptographic keypair and/or network. */ - providers?: SSXProviders; - /** Optional session configuration for the SIWE message. */ - siweConfig?: SiweConfig; - storage?: StorageModule; - /** Whether or not ENS resolution is enabled. True means resolve all on client. */ - resolveEns?: boolean | SSXEnsConfig; -} - -/** Selection and configuration of the storage module. */ -export enum StorageModule { -} - -/** Representation of an active SSXSession. */ -export type SSXSession = { - address: string; - walletAddress: string; - chainId: number; - sessionKey: string; - siwe: string; - signature: string; - ens?: SSXEnsData; -}; - -/** The URL of the server running ssx-server. Providing this field enables SIWE server communication */ -export type ServerHost = string; - -/** The ssx-powered server configuration settings */ -export type SSXProviderServer = { - host: ServerHost; -}; - -/** Web3 provider configuration settings */ -export interface SSXProviderWeb3 { - /** - * window.ethereum for Metamask; - * web3modal.connect() for Web3Modal; - * const signer = useSigner(); const provider = signer.provider; from Wagmi for Rainbowkit - * */ - driver: any; -} - -/** SSX web3 configuration settings */ -export interface SSXProviders { - /** Web3 wallet provider */ - web3?: SSXProviderWeb3; - /** JSON RPC provider configurations */ - rpc?: SSXRPCProvider; - /** Optional reference to server running ssx-server. - * Providing this field enables communication with ssx-server - */ - server?: SSXProviderServer; -} - -export type SSXRPCProvider = SSXEtherscanProvider | SSXInfuraProvider | SSXAlchemyProvider | SSXCloudflareProvider | SSXPocketProvider | SSXAnkrProvider | SSXCustomProvider; - -/** Enum of supported RPC providers */ -export enum SSXRPCProviders { - SSXEtherscanProvider = 'etherscan', - SSXInfuraProvider = 'infura', - SSXAlchemyProvider = 'alchemy', - SSXCloudflareProvider = 'cloudflare', - SSXPocketProvider = 'pocket', - SSXAnkrProvider = 'ankr', - SSXCustomProvider = 'custom', -} - -/** Enum of supported networks for Etherscan */ -export enum SSXEtherscanProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', - KOVAN = 'kovan', -} - -/** Etherscan provider settings */ -export type SSXEtherscanProvider = { - service: SSXRPCProviders.SSXEtherscanProvider; - apiKey?: string | number; - network?: SSXEtherscanProviderNetworks; -}; - -/** Enum of supported networks for Infura */ -export enum SSXInfuraProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', - KOVAN = 'kovan', - POLYGON = 'matic', - POLYGON_MUMBAI = 'maticmum', - OPTIMISM = 'optimism', - OPTIMISM_KOVAN = 'optimism-kovan', - ARBITRUM = 'arbitrum', - ARBITRUM_RINKEBY = 'arbitrum-rinkeby', -} - -/** Infura provider project settings */ -export type SSXInfuraProviderProjectSettings = { - projectId: string; - projectSecret: string; -}; - -/** Infura provider settings */ -export type SSXInfuraProvider = { - service: SSXRPCProviders.SSXInfuraProvider; - apiKey: string | SSXInfuraProviderProjectSettings; - network?: SSXInfuraProviderNetworks; -}; - -/** Enum of supported networks for Alchemy */ -export enum SSXAlchemyProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', - KOVAN = 'kovan', - POLYGON = 'matic', - POLYGON_MUMBAI = 'maticmum', - OPTIMISM = 'optimism', - OPTIMISM_KOVAN = 'optimism-kovan', - ARBITRUM = 'arbitrum', - ARBITRUM_RINKEBY = 'arbitrum-rinkeby', -} - -/** Alchemy provider settings */ -export type SSXAlchemyProvider = { - service: SSXRPCProviders.SSXAlchemyProvider; - apiKey?: string | number; - network?: SSXAlchemyProviderNetworks; -}; - -/** Cloudflare provider settings */ -export type SSXCloudflareProvider = { - service: SSXRPCProviders.SSXCloudflareProvider; -}; - -/** Enum of supported networks for Pocket */ -export enum SSXPocketProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', -} - -/** Pocket provider settings */ -export type SSXPocketProvider = { - service: SSXRPCProviders.SSXPocketProvider; - apiKey?: string | number; - network?: SSXPocketProviderNetworks; -}; - -/** Enum of supported networks for Ankr */ -export enum SSXAnkrProviderNetworks { - MAINNET = 'homestead', - POLYGON = 'matic', - ARBITRUM = 'arbitrum', -} - -/** Ankr provider settings */ -export type SSXAnkrProvider = { - service: SSXRPCProviders.SSXAnkrProvider; - apiKey?: string | number; - network?: SSXAnkrProviderNetworks; -}; - -/** Custom provider settings */ -export type SSXCustomProvider = { - service: SSXRPCProviders.SSXCustomProvider; - url?: string | ConnectionInfo; - network?: providers.Networkish; -}; - -/** Optional session configuration for the SIWE message. */ -export interface SiweConfig extends Partial { } - -/** A Storage module. */ -export interface Storage { - /** Retrieve a value from storage. */ - get(key: string): Promise, - /** Insert a key-value pair into storage. */ - put(key: string, value: any): Promise, - /** List the stored values by key, optionally filtered by prefix. */ - list(prefix?: string): Promise, - /** Delete a value from storage. */ - delete(key: string): Promise, -} - -/** ENS data supported by SSX. */ -export interface SSXEnsData { - /** ENS name/domain. */ - domain?: string | null, - /** ENS avatar. */ - avatarUrl?: string | null -} - -/** ENS options supported by SSX. */ -export interface SSXEnsResolveOptions { - /** Enable ENS name/domain resolution. */ - domain?: boolean; - /** Enable ENS avatar resolution. */ - avatar?: boolean; -} - -/** ENS options supported by SSX. */ -export interface SSXEnsConfig { - /** Enable the ENS resolution on server instead of on client. */ - resolveOnServer?: boolean; - /** ENS resolution options. True means resolve all. */ - resolve: SSXEnsResolveOptions; -} \ No newline at end of file diff --git a/packages/ssx-sdk/tsconfig.json b/packages/ssx-sdk/tsconfig.json index efee40a1..506bc079 100644 --- a/packages/ssx-sdk/tsconfig.json +++ b/packages/ssx-sdk/tsconfig.json @@ -1,102 +1,13 @@ { - "include": ["src/**/*"], - "exclude": ["node_modules/", "dist/**/*"], + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/", + "dist/**/*" + ], "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - "lib": ["es2019"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ } } diff --git a/packages/ssx-server/.eslintrc b/packages/ssx-server/.eslintrc index 15880450..3a26ab40 100644 --- a/packages/ssx-server/.eslintrc +++ b/packages/ssx-server/.eslintrc @@ -1,14 +1,8 @@ { - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - }, "extends": [ - "plugin:@typescript-eslint/recommended", - "plugin:prettier/recommended" + "../../.eslintrc", ], - "rules": { - // e.g. "@typescript-eslint/explicit-function-return-type": "off", - } + "env": { + "node": true, + }, } diff --git a/packages/ssx-server/api-extractor.json b/packages/ssx-server/api-extractor.json index 8db7226c..c97ca777 100644 --- a/packages/ssx-server/api-extractor.json +++ b/packages/ssx-server/api-extractor.json @@ -60,7 +60,7 @@ * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been * local files for library1. */ - "bundledPackages": ["@spruceid/ssx-wasm"], // fix TSdoc Errors in SIWE we can have relevant siwe docs generated as well + "bundledPackages": ["@spruceid/ssx-core"], // fix TSdoc Errors in SIWE we can have relevant siwe docs generated as well // "bundledPackages": ["@spruceid/siwe", "@spruceid/ssx-wasm"], /** diff --git a/packages/ssx-server/package.json b/packages/ssx-server/package.json index 09520f26..3e9863c9 100644 --- a/packages/ssx-server/package.json +++ b/packages/ssx-server/package.json @@ -23,6 +23,7 @@ "ssx-server": "bin/ssx-server.js" }, "dependencies": { + "@spruceid/ssx-core": "*", "@spruceid/ssx-gnosis-extension": "*", "axios": "^0.27.2", "body-parser": "^1.20.0", diff --git a/packages/ssx-server/src/index.ts b/packages/ssx-server/src/index.ts index 5795a338..81103fcb 100644 --- a/packages/ssx-server/src/index.ts +++ b/packages/ssx-server/src/index.ts @@ -1,3 +1,10 @@ export * from './server'; -export * from './types'; export * from './middlewares'; +export * from '@spruceid/ssx-core/dist/types'; +export * from '@spruceid/ssx-core/dist/server/types'; +export { + /** @deprecated use SSXServerConfig field instead */ + SSXServerConfig as SSXConfig, + /** @deprecated use SSXServerProviders field instead */ + SSXServerProviders as SSXProviders +} from '@spruceid/ssx-core'; \ No newline at end of file diff --git a/packages/ssx-server/src/middlewares/express/endpoints.ts b/packages/ssx-server/src/middlewares/express/endpoints.ts index 1b2be9e6..1e023c4a 100644 --- a/packages/ssx-server/src/middlewares/express/endpoints.ts +++ b/packages/ssx-server/src/middlewares/express/endpoints.ts @@ -30,49 +30,54 @@ const ssxEndpoints = (ssx: SSXServer) => { * @param {Response} res */ router.post('/ssx-login', async function (req: Request, res: Response) { - try { - if (!req.body) { - res.status(422).json({ message: 'Expected body.' }); - return; - } - if (!req.body.signature) { - res - .status(422) - .json({ message: 'Expected the field `signature` in body.' }); - return; - } - if (!req.body.siwe) { - res - .status(422) - .json({ message: 'Expected the field `siwe` in the body.' }); - return; - } + if (!req.body) { + res.status(422).json({ message: 'Expected body.' }); + return; + } + if (!req.body.signature) { + res + .status(422) + .json({ message: 'Expected the field `signature` in body.' }); + return; + } + if (!req.body.siwe) { + res + .status(422) + .json({ message: 'Expected the field `siwe` in the body.' }); + return; + } - const { success, error, session } = await ssx.login( + let ssxLoginResponse; + + try { + ssxLoginResponse = await ssx.login( req.body.siwe, req.body.signature, req.body.daoLogin, req.body.resolveEns, req.session.nonce, ); + } catch (error) { + return res.status(500).json({ message: error.message }); + } + + const { success, error, session } = ssxLoginResponse; - if (!success) { - let message: string = error.type; - if (error.expected && error.received) { - message += ` Expected: ${error.expected}. Received: ${error.received}`; - } - return res.status(400).json({ message }); + if (!success) { + let message: string = error.type; + if (error.expected && error.received) { + message += ` Expected: ${error.expected}. Received: ${error.received}`; } + return res.status(400).json({ message }); + } - req.session.siwe = session.siwe; - req.session.signature = session.signature; - req.session.daoLogin = session.daoLogin; - req.session.ens = session.ens; + req.session.siwe = session.siwe; + req.session.signature = session.signature; + req.session.daoLogin = session.daoLogin; + req.session.ens = session.ens; - res.status(200).json({ ...req.session }); - } catch (error) { - res.status(500).json({ message: error.message }); - } + res.status(200).json({ ...req.session }); + return; }); /** @@ -84,12 +89,16 @@ const ssxEndpoints = (ssx: SSXServer) => { router.post('/ssx-logout', async function (req: Request, res: Response) { try { req.session.destroy(null); - req.session = null; + } catch (error) { + res.status(500).json({ message: error.message }); + } + req.session = null; + try { await req.ssx.logout(); - res.status(204).send(); } catch (error) { res.status(500).json({ message: error.message }); } + res.status(204).send(); }); return router; diff --git a/packages/ssx-server/src/middlewares/express/middleware.ts b/packages/ssx-server/src/middlewares/express/middleware.ts index 988f3f5e..21be6f60 100644 --- a/packages/ssx-server/src/middlewares/express/middleware.ts +++ b/packages/ssx-server/src/middlewares/express/middleware.ts @@ -1,7 +1,7 @@ import { SSXServer } from '../../server'; import { SiweMessage } from 'siwe'; import { NextFunction, Request, Response } from 'express'; -import { SSXEnsData, SSXLogFields } from '../../types'; +import { SSXEnsData, SSXLogFields } from '@spruceid/ssx-core'; import { SiweGnosisVerify } from '@spruceid/ssx-gnosis-extension'; declare global { @@ -87,28 +87,28 @@ export const ssxMiddleware = (ssx: SSXServer) => { }; if (req.session?.siwe) { + const { signature, siwe, daoLogin, nonce } = req.session; + let siweMessageVerify; try { - const { signature, siwe, daoLogin, nonce } = req.session; - const { success: verified, data } = await new SiweMessage(siwe).verify( + siweMessageVerify = await new SiweMessage(siwe).verify( { signature, nonce }, { verificationFallback: daoLogin ? SiweGnosisVerify : null, provider: ssx.provider, }, ); - - if (verified) { - req.ssx = { - ...req.ssx, - siwe: data, - verified, - userId: `did:pkh:eip155:${siwe.chainId}:${siwe.address}`, - }; - } else { - req.session.destroy(() => next()); - } } catch (error) { - // ignore errors? Log them? + } + const { success: verified, data } = siweMessageVerify; + if (verified) { + req.ssx = { + ...req.ssx, + siwe: data, + verified, + userId: `did:pkh:eip155:${siwe.chainId}:${siwe.address}`, + }; + } else { + req.session.destroy(() => next()); } } next(); diff --git a/packages/ssx-server/src/middlewares/http/index.ts b/packages/ssx-server/src/middlewares/http/index.ts index b93573f0..f4159a0c 100644 --- a/packages/ssx-server/src/middlewares/http/index.ts +++ b/packages/ssx-server/src/middlewares/http/index.ts @@ -42,7 +42,7 @@ function getBody(req: IncomingMessage): Promise { */ export const SSXHttpMiddleware = (ssx: SSXServer) => { // eslint-disable-next-line @typescript-eslint/no-empty-function - return (requestListener = (req, res) => {}) => { + return (requestListener = (req, res) => { }) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any return async (req: IncomingMessage, res: ServerResponse) => { // session middleware @@ -57,9 +57,10 @@ export const SSXHttpMiddleware = (ssx: SSXServer) => { }; if (req.session?.siwe) { + const { signature, siwe, daoLogin, nonce } = req.session; + let siweMessageVerification; try { - const { signature, siwe, daoLogin, nonce } = req.session; - const { success: verified, data } = await new SiweMessage( + siweMessageVerification = await new SiweMessage( siwe, ).verify( { signature, nonce }, @@ -68,19 +69,18 @@ export const SSXHttpMiddleware = (ssx: SSXServer) => { provider: ssx.provider, }, ); - - if (verified) { - req.ssx = { - ...req.ssx, - siwe: data, - verified, - userId: `did:pkh:eip155:${siwe.chainId}:${siwe.address}`, - }; - } else { - req.session.destroy(() => {}); - } } catch (error) { - // ignore errors? Log them? + } + const { success: verified, data } = siweMessageVerification; + if (verified) { + req.ssx = { + ...req.ssx, + siwe: data, + verified, + userId: `did:pkh:eip155:${siwe.chainId}:${siwe.address}`, + }; + } else { + req.session.destroy(() => { }); } } diff --git a/packages/ssx-server/src/server.ts b/packages/ssx-server/src/server.ts index 6c2167b2..f306c79b 100644 --- a/packages/ssx-server/src/server.ts +++ b/packages/ssx-server/src/server.ts @@ -1,8 +1,16 @@ import { generateNonce, SiweError, SiweMessage } from 'siwe'; import { SiweGnosisVerify } from '@spruceid/ssx-gnosis-extension'; import axios, { AxiosInstance } from 'axios'; -import { SSXLogFields, SSXServerConfig, SSXEventLogTypes, SSXEnsData, SSXEnsResolveOptions } from './types'; -import { getProvider } from './utils'; +import { + SSXLogFields, + SSXServerConfig, + SSXEventLogTypes, + SSXEnsData, + SSXEnsResolveOptions, + ssxLog, + ssxResolveEns, + getProvider +} from '@spruceid/ssx-core'; import { ethers, utils } from 'ethers'; import { SessionData, SessionOptions } from 'express-session'; import session from 'express-session'; @@ -13,13 +21,15 @@ import { EventEmitter } from 'events'; * SSX-Server is a server-side library made to work with the SSX client libraries. * SSX-Server is the base class that takes in a configuration object and works * with various middleware libraries to add authentication and metrics to your server. - * - **/ + */ export class SSXServer extends EventEmitter { + /** SSXServerConfig object. */ private _config: SSXServerConfig; + /** Axios instance. */ private _api: AxiosInstance; + /** EthersJS provider. */ public provider: ethers.providers.BaseProvider; - /** session is a configured instance of express-session middleware */ + /** Session is a configured instance of express-session middleware. */ public session: RequestHandler; constructor(config: SSXServerConfig = {}) { @@ -50,32 +60,22 @@ export class SSXServer extends EventEmitter { } } - /** Set default values for optional configurations */ - private _setDefaults = () => { + /** + * Sets default values for optional configurations + */ + private _setDefaults = (): void => { this._config = {}; this._config.providers = {}; this._config.useSecureCookies = process.env.NODE_ENV === 'production'; }; - /** - * Abstracts the fetch API to append correct headers, host and parse - * responses to JSON for POST requests. + /** + * Registers a new event to the API + * @param data - SSXLogFields object. + * @returns True (success) or false (fail). */ - private _post = (route: string, body: any): Promise => { - return this._api - .post(route, typeof body === 'string' ? body : JSON.stringify(body)) - .then((res) => res.status === 204) - .catch((e) => { - return false; - }); - }; - - /** Registers a new event to the API */ public log = async (data: SSXLogFields): Promise => { - if (!data.timestamp) data.timestamp = new Date().toISOString(); - return ( - this._config.providers?.metrics?.apiKey && this._post('/events', data) - ); + return ssxLog(this._api, this._config.providers?.metrics?.apiKey, data); }; /** @@ -89,6 +89,12 @@ export class SSXServer extends EventEmitter { /** * Verifies the SIWE message, signature, and nonce for a sign-in request. * If the message is verified, a session token is generated and returned. + * @param siwe - SIWE Message. + * @param signature - The signature of the SIWE message. + * @param daoLogin - Whether or not daoLogin is enabled. + * @param resolveEns - Resolve ENS settings. + * @param nonce - nonce string. + * @returns Request data with SSX Server Session. */ public login = async ( siwe: SiweMessage | string, @@ -101,86 +107,79 @@ export class SSXServer extends EventEmitter { error: SiweError; session: Partial; }> => { - // TODO(w4ll3): Refactor this function. - let smartContractWalletOrCustomMethod = false; - try { - const siweMessage = new SiweMessage(siwe); - let siweMessageVerifyPromise: any = siweMessage.verify( - { signature, nonce }, - { - verificationFallback: daoLogin ? SiweGnosisVerify : undefined, - provider: this.provider, - }, - ) - .then(data => data) - .catch(error => { - console.error(error); - throw error; - }); + const siweMessage = new SiweMessage(siwe); - let ens: SSXEnsData = {}; - let promises: Array> = [siweMessageVerifyPromise]; - if (resolveEns) { - let resolveEnsOpts; - if (resolveEns !== true) { - resolveEnsOpts = resolveEns; - } - promises.push(this.resolveEns(siweMessage.address, resolveEnsOpts)); - } - try { - siweMessageVerifyPromise = await Promise.all(promises) - .then(([siweMessageVerify, ensData]) => { - ens = ensData - return siweMessageVerify; - }); - } catch (error) { + let siweMessageVerifyPromise: any = siweMessage.verify( + { signature, nonce }, + { + verificationFallback: daoLogin ? SiweGnosisVerify : undefined, + provider: this.provider, + }, + ) + .then(data => data) + .catch(error => { console.error(error); + throw error; + }); + + let ens: SSXEnsData = {}; + let promises: Array> = [siweMessageVerifyPromise]; + if (resolveEns) { + let resolveEnsOpts; + if (resolveEns !== true) { + resolveEnsOpts = resolveEns; } + promises.push(this.resolveEns(siweMessage.address, resolveEnsOpts)); + } + try { + siweMessageVerifyPromise = await Promise.all(promises) + .then(([siweMessageVerify, ensData]) => { + ens = ensData + return siweMessageVerify; + }); + } catch (error) { + console.error(error); + } - const { success, error, data } = siweMessageVerifyPromise; + const { success, error, data } = siweMessageVerifyPromise; + let smartContractWalletOrCustomMethod = false; + try { + // TODO: Refactor this function. /** This addresses the cases where having DAOLogin * enabled would make all the logs to be of Gnosis Type **/ smartContractWalletOrCustomMethod = !( utils.verifyMessage(data.prepareMessage(), signature) === data.address ); + } catch (error) { + console.error(error); + } - const event = { - userId: `did:pkh:eip155:${data.chainId}:${data.address}`, - type: SSXEventLogTypes.LOGIN, - content: { - signature, - siwe, - isGnosis: daoLogin && smartContractWalletOrCustomMethod, - }, - }; + const event = { + userId: `did:pkh:eip155:${data.chainId}:${data.address}`, + type: SSXEventLogTypes.LOGIN, + content: { + signature, + siwe, + isGnosis: daoLogin && smartContractWalletOrCustomMethod, + }, + }; - this.log(event); - this.emit(event.type, event); + this.log(event); + this.emit(event.type, event); - return { - success, - error, - session: { - siwe: new SiweMessage(siwe), - signature: signature, - daoLogin: daoLogin, - ens, - }, - }; - } catch (e) { - return { - success: false, - error: e, - session: { - siwe: new SiweMessage(siwe), - signature: signature, - daoLogin: daoLogin, - }, - }; - } + return { + success, + error, + session: { + siwe: new SiweMessage(siwe), + signature: signature, + daoLogin: daoLogin, + ens, + }, + }; }; /** @@ -192,47 +191,17 @@ export class SSXServer extends EventEmitter { public resolveEns = async ( /* User Address */ address: string, - resolveEnsOpts: { - /* Enables ENS domain/name resolution */ - domain?: boolean, - /* Enables ENS avatar resolution */ - avatar?: boolean, - } = { - domain: true, - avatar: true - } + /* ENS resolution settings */ + resolveEnsOpts?: SSXEnsResolveOptions ): Promise => { - if (!address) { - throw new Error('Missing address.'); - } - let ens: SSXEnsData = {}; - let promises: Array> = []; - if (resolveEnsOpts?.domain) { - promises.push(this.provider.lookupAddress(address)) - } - if (resolveEnsOpts?.avatar) { - promises.push(this.provider.getAvatar(address)) - } - - await Promise.all(promises) - .then(([domain, avatarUrl]) => { - if (!resolveEnsOpts.domain && resolveEnsOpts.avatar) { - [domain, avatarUrl] = [undefined, domain]; - } - if (domain) { - ens['domain'] = domain; - } - if (avatarUrl) { - ens['avatarUrl'] = avatarUrl; - } - }); - - return ens; - } + return ssxResolveEns(this.provider, address, resolveEnsOpts) + }; /** * Logs out the user by deleting the session. * Currently this is a no-op. + * @param destroySession - Method to remove session from storage. + * @returns Promise with true (success) or false (fail). */ public logout = async ( destroySession?: () => Promise, @@ -240,6 +209,10 @@ export class SSXServer extends EventEmitter { return destroySession?.(); }; + /** + * Gets Express Session config params to configure the session. + * @returns Session options. + */ public getExpressSessionConfig = (): SessionOptions => { return { ...this.getDefaultExpressSessionConfig(), @@ -250,6 +223,10 @@ export class SSXServer extends EventEmitter { }; }; + /** + * Gets default Express Session Config. + * @returns Default session options + */ private getDefaultExpressSessionConfig = (): SessionOptions => ({ name: 'ssx-session-storage', secret: this._config.signingKey, diff --git a/packages/ssx-server/src/utils.ts b/packages/ssx-server/src/utils.ts deleted file mode 100644 index 646ddfc4..00000000 --- a/packages/ssx-server/src/utils.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - isSSXAlchemyProvider, - isSSXAnkrProvider, - isSSXCloudflareProvider, - isSSXCustomProvider, - isSSXEtherscanProvider, - isSSXInfuraProvider, - isSSXPocketProvider, - SSXAlchemyProviderNetworks, - SSXAnkrProviderNetworks, - SSXEtherscanProviderNetworks, - SSXInfuraProviderNetworks, - SSXPocketProviderNetworks, - SSXRPCProvider, -} from './types'; -import { ethers } from 'ethers'; - -/** - * Returns an ethers provider based on the RPC configuration - */ -export const getProvider = (rpc: SSXRPCProvider): ethers.providers.BaseProvider => { - if (isSSXEtherscanProvider(rpc)) { - return new ethers.providers.EtherscanProvider( - rpc.network ?? SSXEtherscanProviderNetworks.MAINNET, - rpc.apiKey, - ); - } - if (isSSXInfuraProvider(rpc)) { - return new ethers.providers.InfuraProvider( - rpc.network ?? SSXInfuraProviderNetworks.MAINNET, - rpc.apiKey, - ); - } - if (isSSXAlchemyProvider(rpc)) { - return new ethers.providers.AlchemyProvider( - rpc.network ?? SSXAlchemyProviderNetworks.MAINNET, - rpc.apiKey, - ); - } - if (isSSXCloudflareProvider(rpc)) { - return new ethers.providers.CloudflareProvider(); - } - if (isSSXPocketProvider(rpc)) { - return new ethers.providers.PocketProvider( - rpc.network ?? SSXPocketProviderNetworks.MAINNET, - rpc.apiKey, - ); - } - if (isSSXAnkrProvider(rpc)) { - return new ethers.providers.AnkrProvider( - rpc.network ?? SSXAnkrProviderNetworks.MAINNET, - rpc.apiKey, - ); - } - if (isSSXCustomProvider(rpc)) { - return new ethers.providers.JsonRpcProvider(rpc.url, rpc.network); - } -}; diff --git a/packages/ssx-server/tsconfig.json b/packages/ssx-server/tsconfig.json index b2b9bb9a..0bf32b40 100644 --- a/packages/ssx-server/tsconfig.json +++ b/packages/ssx-server/tsconfig.json @@ -1,19 +1,13 @@ { - - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"], + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/", + "dist/**/*" + ], "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Projects */ - // "incremental": true, /* Enable incremental compilation */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ "target": "ES5" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "lib": [ "es2019", @@ -21,89 +15,9 @@ "ES6", "DOM" ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */, - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ - // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - - /* Modules */ - "module": "CommonJS" /* Specify what module code is generated. */, "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ "rootDir": "./src", /* Specify the root folder within your source files. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "resolveJsonModule": true, /* Enable importing .json files */ - // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ - - /* Emit */ - "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, - "declarationMap": true /* Create sourcemaps for d.ts files. */, - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true /* Create source map files for emitted JavaScript files. */, - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ "outDir": "./dist" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ - // "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ - // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ - // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ } } diff --git a/packages/ssx-serverless/.eslintrc b/packages/ssx-serverless/.eslintrc index f6ad8eeb..77983042 100644 --- a/packages/ssx-serverless/.eslintrc +++ b/packages/ssx-serverless/.eslintrc @@ -1,13 +1,5 @@ { - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ], "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "prettier" - ] -} \ No newline at end of file + "../../.eslintrc", + ], +} diff --git a/packages/ssx-serverless/api-extractor.json b/packages/ssx-serverless/api-extractor.json index 21082dfe..c7ea0969 100644 --- a/packages/ssx-serverless/api-extractor.json +++ b/packages/ssx-serverless/api-extractor.json @@ -60,7 +60,7 @@ * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been * local files for library1. */ - // "bundledPackages": ["@spruceid/ssx-gnosis-extension"], // fix TSdoc Errors in SIWE we can have relevant siwe docs generated as well + "bundledPackages": ["@spruceid/ssx-core"], // fix TSdoc Errors in SIWE we can have relevant siwe docs generated as well /** * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. diff --git a/packages/ssx-serverless/package.json b/packages/ssx-serverless/package.json index 2a149e40..0a72fe4d 100644 --- a/packages/ssx-serverless/package.json +++ b/packages/ssx-serverless/package.json @@ -28,8 +28,9 @@ }, "dependencies": { "siwe": "^2.1.2", + "@spruceid/ssx-core": "*", "@spruceid/ssx-gnosis-extension": "*", - "axios": "^1.1.3" + "axios": "^0.27.2" }, "devDependencies": { "@microsoft/api-documenter": "^7.19.4", diff --git a/packages/ssx-serverless/src/index.ts b/packages/ssx-serverless/src/index.ts index b9952f37..552916c3 100644 --- a/packages/ssx-serverless/src/index.ts +++ b/packages/ssx-serverless/src/index.ts @@ -1,27 +1,41 @@ -import { generateNonce, SiweError, SiweMessage, SiweResponse } from 'siwe'; +import { generateNonce, SiweMessage } from 'siwe'; import { SiweGnosisVerify } from '@spruceid/ssx-gnosis-extension'; import axios, { AxiosInstance } from 'axios'; -import { SSXLogFields, SSXServerConfig, SSXEventLogTypes, SSXSessionCRUDConfig, SSXSessionData, SSXEnsData } from './types'; -import { getProvider } from './utils'; +import { + SSXServerConfig, + SSXSessionCRUDConfig, + SSXSessionData, + SSXEnsData as ISSXEnsData +} from './types'; +import { + SSXLogFields, + SSXEventLogTypes, + ssxLog, + SSXEnsResolveOptions, + SSXEnsData, + getProvider, + ssxResolveEns +} from '@spruceid/ssx-core'; import { ethers, utils } from 'ethers'; /** * SSX-Server is a server-side library made to work with the SSX client libraries. * SSX-Server is the base class that takes in a configuration object to add * authentication and metrics to your server. - * - **/ + */ export class SSXServer { + /** SSXServerConfig object. */ private _config: SSXServerConfig = {}; + /** Axios instance. */ private _api: AxiosInstance; - /** EthersJS provider */ + /** EthersJS provider. */ public provider: ethers.providers.BaseProvider; - /** Definition of CRUD functions for sessions */ + /** Definition of CRUD functions for sessions. */ public session: SSXSessionCRUDConfig; /** - * @param config - Base configuration of the SSXServer - * @param session - CRUD definition for session operations + * @param config - Base configuration of the SSXServer. + * @param session - CRUD definition for session operations. * @example * ``` * const ssx = new SSXServer({ @@ -66,24 +80,14 @@ export class SSXServer { this.provider = getProvider(config.providers?.rpc); } - /** - * Abstracts the fetch API to append correct headers, host and parse - * responses to JSON for POST requests. + /** + * Registers a new event to the API. + * @param data - SSXLogFields JSON. + * @returns Promise with true (success) or false (fail). */ - private _post = (route: string, body: any): Promise => { - return this._api - .post(route, typeof body === 'string' ? body : JSON.stringify(body)) - .then((res) => res.status === 204) - .catch((e) => { - console.error(e); - return false; - }); - }; - - /** Registers a new event to the API */ public log = async (data: SSXLogFields): Promise => { - if (!data.timestamp) { data.timestamp = new Date().toISOString() }; - return !!this._config.providers?.metrics?.apiKey && this._post('/events', data); + this._api; + return ssxLog(this._api, this._config.providers?.metrics?.apiKey ?? '', data); }; /** @@ -94,33 +98,114 @@ export class SSXServer { */ public generateNonce = generateNonce; + /** + * Tries to update the session to store the new nonce. + * @param sessionKey - Session identifier. + * @param value - Value to update statement. + * @param opts - Optional parameters. + * @returns Object with update result. + */ + private updateSessionNonce = async ( + /* The session with this key will be updated. */ + sessionKey: string, + /** Value for the update statement. */ + value: any, + /* Optional parameters to be passed to session.update. */ + opts: any + ): Promise<{ + success: boolean; + dbResult: any; + }> => { + let dbResult; + try { + dbResult = await this.session.update(sessionKey, value, opts); + } catch (error) { + throw { + success: false, + dbResult: error + }; + } + return { + success: true, + dbResult + }; + }; + + /** + * Tries to create a session to store and store a nonce. + * @param value - Value for the create statement. + * @param opts - Optional parameters. + * @returns Object with create result. + */ + private createSessionNonce = async ( + /** Value for the create statement */ + value: any, + /* Optional parameters to be passed to session.create. */ + opts: any + ): Promise<{ + success: boolean; + dbResult: any; + }> => { + let dbResult; + try { + dbResult = await this.session.create(value, opts); + } catch (error) { + throw { + success: false, + dbResult: error + }; + } + return { + success: true, + dbResult + }; + }; + /** * Generates a nonce and stores it in the current session * if a sessionKey is provided or creates a new one if not. + * @param getNonceOpts - Optional params to configure session management. + * @returns Promise with nonce and database result. */ public getNonce = async ( getNonceOpts?: { - /* If provided the session with this key will be updated */ + /* If provided the session with this key will be updated. */ sessionKey?: any, - /* A function that will return the value for the update statement */ + /* A function that will return the value for the update statement. */ generateUpdateValue?: (nonce: string) => any, - /* A function that will return the value for the create statement */ + /* A function that will return the value for the create statement. */ generateCreateValue?: (nonce: string) => any, - /* Optional parameters to be passed to session.create */ + /* Optional parameters to be passed to session.create. */ createOpts?: Record, - /* Optional parameters to be passed to session.update */ + /* Optional parameters to be passed to session.update. */ updateOpts?: Record, }): Promise<{ nonce: string, dbResult: any }> => { const nonce = generateNonce(); let dbResult; if (getNonceOpts) { - try { + + let updateSessionResponse; + if (getNonceOpts?.sessionKey) { const updateValue = getNonceOpts.generateUpdateValue?.(nonce) ?? nonce; - dbResult = await this.session.update(getNonceOpts.sessionKey, updateValue, getNonceOpts?.updateOpts); - } catch (error) { + try { + updateSessionResponse = await this.updateSessionNonce(getNonceOpts?.sessionKey, updateValue, getNonceOpts?.updateOpts); + } catch (error) { + updateSessionResponse = error; + } + } + /* + If I don't have a sessionKey (to update) + OR + I have a sessionKey (to update) but the update returned success=false + */ + if (!getNonceOpts.sessionKey || (getNonceOpts.sessionKey && !updateSessionResponse?.success)) { const createValue = getNonceOpts.generateCreateValue?.(nonce) ?? { nonce, address: getNonceOpts.sessionKey }; - dbResult = await this.session.create(createValue, getNonceOpts?.createOpts); + try { + dbResult = (await this.createSessionNonce(createValue, getNonceOpts?.createOpts)).dbResult; + } catch (error) { + dbResult = error.dbResult; + } } } @@ -130,29 +215,29 @@ export class SSXServer { /** * Verifies the SIWE message, signature, and nonce for a sign-in request. * If the message is verified, a session token is generated and returned. - * @param siwe - Object containing the siwe fields or EIP-4361 message - * @param signature - Signature of the EIP-4361 message - * @param sessionKey - Key used to index user's session - * @param signInOpts - Additional options to customize sign-in behavior - * @returns Object containing information about the session + * @param siwe - Object containing the siwe fields or EIP-4361 message. + * @param signature - Signature of the EIP-4361 message. + * @param sessionKey - Key used to index user's session. + * @param signInOpts - Additional options to customize sign-in behavior. + * @returns Object containing information about the session. */ public signIn = async ( siwe: SiweMessage | string, signature: string, - /* Session key to be used in session lookup */ + /* Session key to be used in session lookup. */ sessionKey: any, signInOpts: { - /* Enables lookup for delegations */ + /* Enables lookup for delegations. */ daoLogin?: boolean, - /* Enables ENS Domain resolution */ + /* Enables ENS Domain resolution. */ resolveEnsDomain?: boolean, - /* Enables ENS Avatar resolution */ + /* Enables ENS Avatar resolution. */ resolveEnsAvatar?: boolean, - /* Optional parameters to be passed to session.retrieve */ + /* Optional parameters to be passed to session.retrieve. */ retrieveOpts?: Record, - /* A function that will return the value for the update statement */ + /* A function that will return the value for the update statement. */ generateUpdateValue?: (sessionData: SSXSessionData) => any, - /* Optional parameters to be passed to session.create */ + /* Optional parameters to be passed to session.create. */ updateOpts?: Record, }, ): Promise => { @@ -179,25 +264,25 @@ export class SSXServer { throw error; }); - let ens: SSXEnsData = {}; + let ens: ISSXEnsData = {}; let promises: Array> = [siweMessageVerifyPromise]; - try { - if (signInOpts?.resolveEnsDomain) { - promises.push(this.provider.lookupAddress(siweMessage.address)) - } - if (signInOpts?.resolveEnsAvatar) { - promises.push(this.provider.getAvatar(siweMessage.address)) + + if (signInOpts?.resolveEnsDomain || signInOpts?.resolveEnsAvatar) { + const resolveEnsOpts = { + domain: signInOpts?.resolveEnsDomain, + avatar: signInOpts?.resolveEnsAvatar, } + promises.push(this.resolveEns(siweMessage.address, resolveEnsOpts)); + } + + try { siweMessageVerifyPromise = await Promise.all(promises) - .then(([siweMessageVerify, ensName, ensAvatarUrl]) => { - if (!signInOpts.resolveEnsDomain && signInOpts.resolveEnsAvatar) { - [ensName, ensAvatarUrl] = [undefined, ensName]; + .then(([siweMessageVerify, ensData]) => { + if (ensData.domain) { + ens['ensName'] = ensData.domain; } - if (ensName) { - ens['ensName'] = ensName; - } - if (ensAvatarUrl) { - ens['ensAvatarUrl'] = ensAvatarUrl; + if (ensData.avatarUrl) { + ens['ensAvatarUrl'] = ensData.avatarUrl; } return siweMessageVerify; }); @@ -216,45 +301,60 @@ export class SSXServer { ens }; + const updateValue = signInOpts.generateUpdateValue?.(sessionData) ?? sessionData; try { - const updateValue = signInOpts.generateUpdateValue?.(sessionData) ?? sessionData; await this.session.update(sessionKey, updateValue, signInOpts?.updateOpts); } catch (error) { console.error(error); throw error; } + let smartContractWalletOrCustomMethod = false; try { - // TODO(w4ll3): Refactor this function. + // TODO: Refactor this function. /** This addresses the cases where having DAOLogin * enabled would make all the logs to be of Gnosis Type **/ - let smartContractWalletOrCustomMethod = false; smartContractWalletOrCustomMethod = !( utils.verifyMessage(siweMessage.prepareMessage(), signature) === siweMessage.address ); - - this.log({ - userId: `did:pkh:eip155:${siweMessage.chainId}:${siweMessage.address}`, - type: SSXEventLogTypes.LOGIN, - content: { - signature, - siwe, - isGnosis: signInOpts?.daoLogin && smartContractWalletOrCustomMethod, - }, - }); } catch (error) { console.error(error); } + this.log({ + userId: `did:pkh:eip155:${siweMessage.chainId}:${siweMessage.address}`, + type: SSXEventLogTypes.LOGIN, + content: { + signature, + siwe, + isGnosis: signInOpts?.daoLogin && smartContractWalletOrCustomMethod, + }, + }); + return sessionData; }; /** - * Calls the delete function to delete the user's session - * @param sessionKey - Key used to index sessions + * ENS data supported by SSX. + * @param address - User address. + * @param resolveEnsOpts - Options to resolve ENS. + * @returns Object containing ENS data. + */ + public resolveEns = async ( + /* User Address */ + address: string, + /* ENS resolution settings */ + resolveEnsOpts?: SSXEnsResolveOptions + ): Promise => { + return ssxResolveEns(this.provider, address, resolveEnsOpts) + }; + + /** + * Calls the delete function to delete the user's session. + * @param sessionKey - Key used to index sessions. * @param deleteOpts - Additional options to be passed to the seeion.delete function. - * @returns The result of session.delete + * @returns The result of session.delete. * @example * signOut("0x9D85ca56217D2bb651b00f15e694EB7E713637D4") */ @@ -271,7 +371,7 @@ export class SSXServer { * @param sessionKey - Key used to index sessions. * @param getSSXDataFromSession - Function that will parse the resolved value from * session into SSXSessionData if the a custom session structure is being used. - * @returns SSXSessionData + * @returns SSXSessionData. */ public me = async (sessionKey: any, getSSXDataFromSession?: (session: any) => SSXSessionData) => { const dbResult = await this.session.retrieve(sessionKey); @@ -279,15 +379,21 @@ export class SSXServer { throw new Error('Unable to retrieve session.'); } let session: SSXSessionData; + let castingError: any; try { session = dbResult as SSXSessionData; } catch (error) { + castingError = error; + } + + if (!session) { if (!getSSXDataFromSession) { - console.error(error); - throw error; + console.error(castingError); + throw castingError; } session = getSSXDataFromSession(dbResult); } + const siweMessage = new SiweMessage(session.siweMessage); await siweMessage.verify( { signature: session.signature }, @@ -300,5 +406,19 @@ export class SSXServer { } } -export * from "./types"; -export * from "./utils"; +export * from "@spruceid/ssx-core/dist/types"; +export { + SSXLogFields, + SSXEventLogTypes +}; +export { + SSXSessionCRUDConfig, + SSXSessionData, + SSXEnsData, + SSXServerConfig, + /** @deprecated use SSXServerConfig field instead */ + SSXServerConfig as SSXConfig, + SSXServerProviders, + /** @deprecated use SSXServerProviders field instead */ + SSXServerProviders as SSXProviders +} from './types'; \ No newline at end of file diff --git a/packages/ssx-serverless/src/types.ts b/packages/ssx-serverless/src/types.ts index 7e0dfc1a..ab03a667 100644 --- a/packages/ssx-serverless/src/types.ts +++ b/packages/ssx-serverless/src/types.ts @@ -1,217 +1,23 @@ -import { providers } from 'ethers'; -import { ConnectionInfo } from 'ethers/lib/utils'; import { SiweMessage } from 'siwe'; +import { SSXRPCProvider, SSXMetricsProvider } from '@spruceid/ssx-core'; +// TODO: unify with ssx-server /** Configuration interface for ssx-server */ export interface SSXServerConfig { /** Connection to a cryptographic keypair and/or network. */ - providers?: SSXProviders; + providers?: SSXServerProviders; /** Enable lookup for delegations in the DelegateRegistry SC*/ daoLogin?: boolean; } /** SSX web3 configuration settings */ -export interface SSXProviders { +export interface SSXServerProviders { /** JSON RPC provider configurations */ rpc?: SSXRPCProvider; /** Metrics service configurations */ metrics?: SSXMetricsProvider; } -export type SSXRPCProvider = - | SSXGenericProvider - | SSXEtherscanProvider - | SSXInfuraProvider - | SSXAlchemyProvider - | SSXCloudflareProvider - | SSXPocketProvider - | SSXAnkrProvider - | SSXCustomProvider; - -/** Enum of supported RPC providers */ -export enum SSXRPCProviders { - SSXAlchemyProvider = 'alchemy', - SSXAnkrProvider = 'ankr', - SSXCloudflareProvider = 'cloudflare', - SSXCustomProvider = 'custom', - SSXEtherscanProvider = 'etherscan', - SSXInfuraProvider = 'infura', - SSXPocketProvider = 'pocket', -} - -/** Enum of supported networks for Etherscan */ -export enum SSXEtherscanProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', - KOVAN = 'kovan', -} - -/** Etherscan provider settings */ -export type SSXEtherscanProvider = { - service: SSXRPCProviders.SSXEtherscanProvider; - apiKey?: string; - network?: SSXEtherscanProviderNetworks; -}; - -/* Type-Guard for SSXEtherScanProvider */ -export const isSSXEtherscanProvider = (provider: SSXRPCProvider): - provider is SSXEtherscanProvider => provider.service === SSXRPCProviders.SSXEtherscanProvider; - -/** Enum of supported networks for Infura */ -export enum SSXInfuraProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', - KOVAN = 'kovan', - POLYGON = 'matic', - POLYGON_MUMBAI = 'maticmum', - OPTIMISM = 'optimism', - OPTIMISM_KOVAN = 'optimism-kovan', - ARBITRUM = 'arbitrum', - ARBITRUM_RINKEBY = 'arbitrum-rinkeby', -} - -/** Infura provider project settings */ -export type SSXInfuraProviderProjectSettings = { - projectId: string; - projectSecret: string; -}; - -/** Infura provider settings */ -export type SSXInfuraProvider = { - service: SSXRPCProviders.SSXInfuraProvider; - apiKey: string | SSXInfuraProviderProjectSettings; - network?: SSXInfuraProviderNetworks; -}; - -/* Type-Guard for SSXInfuraProvider */ -export const isSSXInfuraProvider = (provider: SSXRPCProvider): - provider is SSXInfuraProvider => provider.service === SSXRPCProviders.SSXInfuraProvider; - -/** Enum of supported networks for Alchemy */ -export enum SSXAlchemyProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', - KOVAN = 'kovan', - POLYGON = 'matic', - POLYGON_MUMBAI = 'maticmum', - OPTIMISM = 'optimism', - OPTIMISM_KOVAN = 'optimism-kovan', - ARBITRUM = 'arbitrum', - ARBITRUM_RINKEBY = 'arbitrum-rinkeby', -} - -/** Alchemy provider settings */ -export type SSXAlchemyProvider = { - service: SSXRPCProviders.SSXAlchemyProvider; - apiKey?: string; - network?: SSXAlchemyProviderNetworks; -}; - -/* Type-Guard for SSXAlchemyProvider */ -export const isSSXAlchemyProvider = (provider: SSXRPCProvider): - provider is SSXAlchemyProvider => provider.service === SSXRPCProviders.SSXAlchemyProvider; - -/** Cloudflare provider settings */ -export type SSXCloudflareProvider = { - service: SSXRPCProviders.SSXCloudflareProvider; -}; - -/* Type-Guard for SSXCloudflareProvider */ -export const isSSXCloudflareProvider = (provider: SSXRPCProvider): - provider is SSXCloudflareProvider => provider.service === SSXRPCProviders.SSXCloudflareProvider; - -/** Enum of supported networks for Pocket */ -export enum SSXPocketProviderNetworks { - MAINNET = 'homestead', - ROPSTEN = 'ropsten', - RINKEBY = 'rinkeby', - GOERLI = 'goerli', -} - -/** Pocket provider settings */ -export type SSXPocketProvider = { - service: SSXRPCProviders.SSXPocketProvider; - apiKey?: string; - network?: SSXPocketProviderNetworks; -}; - -/* Type-Guard for SSXPocketProvider */ -export const isSSXPocketProvider = (provider: SSXRPCProvider): - provider is SSXPocketProvider => provider.service === SSXRPCProviders.SSXPocketProvider; - -/** Enum of supported networks for Ankr */ -export enum SSXAnkrProviderNetworks { - MAINNET = 'homestead', - POLYGON = 'matic', - ARBITRUM = 'arbitrum', -} - -/** Ankr provider settings */ -export type SSXAnkrProvider = { - service: SSXRPCProviders.SSXAnkrProvider; - apiKey?: string; - network?: SSXAnkrProviderNetworks; -}; - -/* Type-Guard for SSXAnkrProvider */ -export const isSSXAnkrProvider = (provider: SSXRPCProvider): - provider is SSXAnkrProvider => provider.service === SSXRPCProviders.SSXAnkrProvider; - -/** Custom provider settings */ -export type SSXCustomProvider = { - service: SSXRPCProviders.SSXCustomProvider; - url?: string | ConnectionInfo; - network?: providers.Networkish; -}; - -/* Type-Guard for SSXCustomProvider */ -export const isSSXCustomProvider = (provider: SSXRPCProvider): - provider is SSXCustomProvider => provider.service === SSXRPCProviders.SSXCustomProvider; - -/** Generic provider settings */ -export type SSXGenericProvider = { - service: SSXRPCProviders; - url?: string | ConnectionInfo; - network?: providers.Networkish; - apiKey?: string | SSXInfuraProviderProjectSettings; -}; - -/** SSX Metrics Provider settings */ -export type SSXMetricsProvider = { - service: 'ssx'; - apiKey: string; -}; - -/** Allowed fields for an SSX Log */ -export interface SSXLogFields { - /** Unique identifier for the user, formatted as a DID */ - userId: string; - /** RFC-3339 time of resource generation, defaults to now */ - timestamp?: string; - /** Type of content being logged */ - type: SSXEventLogTypes; - /** Any JSON stringifiable structure to be logged */ - content: string | Record; -} - -/** Available SSX Log Types */ -export enum SSXEventLogTypes { - /** Login type definition */ - LOGIN = 'ssx-login', - /** Logout type definition */ - // LOGOUT = "ssx-logout", - /** Logging type definition */ - // LOG = "LOG", - /** Event type definition */ - // EVENT = "event", -} - /** * Type definition for CRUD session functions * @example diff --git a/packages/ssx-serverless/tsconfig.json b/packages/ssx-serverless/tsconfig.json index b4858dec..af73b91b 100644 --- a/packages/ssx-serverless/tsconfig.json +++ b/packages/ssx-serverless/tsconfig.json @@ -1,104 +1,15 @@ { - "exclude": [ "example", "dist", "node_modules" ], + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules/", + "dist/**/*" + ], "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ } -} + // // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "strict": true, /* Enable all strict type-checking options. */ +} \ No newline at end of file diff --git a/scripts/build-docs.js b/scripts/build-docs.js index db008de2..2f8eeb38 100644 --- a/scripts/build-docs.js +++ b/scripts/build-docs.js @@ -15,7 +15,12 @@ const getLines = (output, prefix = 'reference/ssx-sdk/', layer = 0) => { const generateReference = async () => { const output = {}; - const dirs = [ './documentation/reference/ssx-sdk', './documentation/reference/ssx-server', './documentation/reference/ssx-serverless']; + const dirs = [ + './documentation/reference/ssx-sdk', + './documentation/reference/ssx-core', + './documentation/reference/ssx-server', + './documentation/reference/ssx-serverless', + ]; const docFiles = [ ...(await Promise.all(dirs.map(async dir => await readdir(dir)))) ].reduce((p, c) => [...p, ...c], []); for (const docFile of docFiles) { @@ -58,12 +63,14 @@ const generateReference = async () => { const lines = [ "## Reference", ...getLines({ 'ssx': output['ssx'] }), + ...getLines({ 'ssx-core': output['ssx-core']}, 'reference/ssx-core/'), ...getLines({ 'ssx-server': output['ssx-server']}, 'reference/ssx-server/'), ...getLines({ 'ssx-serverless': output['ssx-serverless']}, 'reference/ssx-serverless/'), ]; let reference = lines.join('\n'); reference = reference.replace('* [ssx]', '* [SSX API Reference]'); + reference = reference.replace('* [ssx-core]', '* [SSX Core API Reference]'); reference = reference.replace('* [ssx-server]', '* [SSX Server API Reference]'); reference = reference.replace('* [ssx-serverless]', '* [SSX Serverless API Reference]'); return reference; @@ -76,6 +83,7 @@ const generateSUMMARY = async () => { let reference = await generateReference(); reference = reference.replace(/^\* \[ssx\]/, '* [SSX API Reference]'); + reference = reference.replace(/^\* \[ssx-core\]/, '* [SSX Core API Reference]'); reference = reference.replace(/^\* \[ssx-server\]/, '* [SSX Server API Reference]'); reference = reference.replace(/^\* \[ssx-serverless\]/, '* [SSX Serverless API Reference]'); await writeFile('./documentation/SUMMARY.md', previous.concat(reference)); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..2fd5432b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": [ + "es2019" + ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "module": "commonjs", /* Specify what module code is generated. */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declarationMap": true, /* Create sourcemaps for d.ts files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index a0cba5d5..0e86aacc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -724,7 +724,28 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" + integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.5" + "@babel/parser" "^7.20.5" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== @@ -763,6 +784,15 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" + integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== + dependencies: + "@babel/types" "^7.20.5" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -788,7 +818,7 @@ browserslist "^4.21.3" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.2": +"@babel/helper-create-class-features-plugin@^7.18.6": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.2.tgz#3c08a5b5417c7f07b5cf3dfb6dc79cbec682e8c2" integrity sha512-k22GoYRAHPYr9I+Gvy2ZQlAe5mGy8BqWst2wRt8cwIufWTxrsVshhIBvYNqC80N0GSFWTsqRVexOtfzlgOEDvA== @@ -801,6 +831,19 @@ "@babel/helper-replace-supers" "^7.19.1" "@babel/helper-split-export-declaration" "^7.18.6" +"@babel/helper-create-class-features-plugin@^7.20.2", "@babel/helper-create-class-features-plugin@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz#327154eedfb12e977baa4ecc72e5806720a85a06" + integrity sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz#7976aca61c0984202baca73d84e2337a5424a41b" @@ -964,6 +1007,15 @@ "@babel/traverse" "^7.20.1" "@babel/types" "^7.20.0" +"@babel/helpers@^7.20.5": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" + integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -978,6 +1030,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== +"@babel/parser@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -1022,11 +1079,11 @@ "@babel/plugin-syntax-class-static-block" "^7.14.5" "@babel/plugin-proposal-decorators@^7.16.4": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.2.tgz#1c6c32b2a44b154ebeec2bb534f9eaebdb541fb6" - integrity sha512-nkBH96IBmgKnbHQ5gXFrcmez+Z9S2EIDKDQGp005ROqBigc88Tky4rzCnlP/lnlj245dCEQl4/YyV0V1kYh5dw== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.5.tgz#28ba1a0e5044664a512967a19407d7fc26925394" + integrity sha512-Lac7PpRJXcC3s9cKsBfl+uc+DYXU5FD06BrTFunQO6QIQT+DwyzDPURAowI3bcvD1dZF/ank1Z5rstUJn3Hn4Q== dependencies: - "@babel/helper-create-class-features-plugin" "^7.20.2" + "@babel/helper-create-class-features-plugin" "^7.20.5" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-replace-supers" "^7.19.1" "@babel/helper-split-export-declaration" "^7.18.6" @@ -1714,14 +1771,21 @@ "@babel/plugin-transform-typescript" "^7.18.6" "@babel/runtime-corejs3@^7.10.2": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.20.1.tgz#d0775a49bb5fba77e42cbb7276c9955c7b05af8d" - integrity sha512-CGulbEDcg/ND1Im7fUNRZdGXmX2MTWVVZacQi/6DiKE5HNwZ3aVTm5PV4lO8HHz0B2h8WQyvKKjbX5XgTtydsg== + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.20.6.tgz#63dae945963539ab0ad578efbf3eff271e7067ae" + integrity sha512-tqeujPiuEfcH067mx+7otTQWROVMKHXEaOQcAeNV5dDdbPWvPcFA8/W9LXw2NfjNmOetqLl03dfnG2WALPlsRQ== dependencies: core-js-pure "^3.25.1" - regenerator-runtime "^0.13.10" + regenerator-runtime "^0.13.11" + +"@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.9.2": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3" + integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA== + dependencies: + regenerator-runtime "^0.13.11" -"@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.4", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": version "7.20.1" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== @@ -1753,7 +1817,23 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/traverse@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== @@ -1762,6 +1842,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.12.6", "@babel/types@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -2001,7 +2090,7 @@ resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4" integrity sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg== -"@csstools/postcss-cascade-layers@^1.1.0": +"@csstools/postcss-cascade-layers@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz#8a997edf97d34071dd2e37ea6022447dd9e795ad" integrity sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA== @@ -4547,9 +4636,9 @@ esquery "^1.0.1" "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": - version "0.5.9" - resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.9.tgz#35aae6624a6270ca7ad755800b7eec417fa6f830" - integrity sha512-7QV4cqUwhkDIHpMAZ9mestSJ2DMIotVTbOUwbiudhjCRTAWWKIaBecELiEM2LT3AHFeOAaHIcFu4dbXjX+9GBA== + version "0.5.10" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" + integrity sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA== dependencies: ansi-html-community "^0.0.8" common-path-prefix "^3.0.0" @@ -4557,7 +4646,7 @@ error-stack-parser "^2.0.6" find-up "^5.0.0" html-entities "^2.1.0" - loader-utils "^2.0.3" + loader-utils "^2.0.4" schema-utils "^3.0.0" source-map "^0.7.3" @@ -4844,9 +4933,10 @@ integrity sha512-PuxRlyTlycXrTU11B22XcCz1PUU7p5krXxnb+ztPkgR3fAMP3ubENgoUyLllUJXxx2O/Jpne+0P9HIro+vilPg== "@spruceid/ssx@file:packages/ssx-sdk": - version "0.1.1" + version "1.0.0" dependencies: "@metamask/detect-provider" "^1.2.0" + "@spruceid/ssx-core" "*" "@spruceid/ssx-gnosis-extension" "*" "@spruceid/ssx-sdk-wasm" "0.1.2" "@types/lodash.merge" "^4.6.7" @@ -5172,13 +5262,20 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.18.2" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.2.tgz#235bf339d17185bdec25e024ca19cce257cc7309" integrity sha512-FcFaxOr2V5KZCviw1TnutEMVUVsGt4D2hP1TAfXZAMKuHYW3xQhe3jTxNPWutgCJ3/X1c5yX8ZoGVEItxKbwBg== dependencies: "@babel/types" "^7.3.0" +"@types/babel__traverse@^7.0.4": + version "7.18.3" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== + dependencies: + "@babel/types" "^7.3.0" + "@types/bn.js@^4.11.3": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -5357,9 +5454,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@*": - version "29.2.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.2.2.tgz#874e7dc6702fa6a3fe6107792aa98636dcc480b4" - integrity sha512-og1wAmdxKoS71K2ZwSVqWPX6OVn3ihZ6ZT2qvZvZQm90lJVDyXIjYcu4Khx2CNIeaFv12rOU/YObOsI3VOkzog== + version "29.2.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.2.3.tgz#f5fd88e43e5a9e4221ca361e23790d48fcf0a211" + integrity sha512-6XwoEbmatfyoCjWRX7z0fKMmgYKe9+/HrviJ5k0X/tjJWHGAezZOfYaxqQKuzG/TvQyr+ktjm4jgbk0s4/oF2w== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -5522,9 +5619,9 @@ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== "@types/react-dom@^18.0.0": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.8.tgz#d2606d855186cd42cc1b11e63a71c39525441685" - integrity sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw== + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504" + integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg== dependencies: "@types/react" "*" @@ -5677,7 +5774,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.23.0", "@typescript-eslint/eslint-plugin@^5.27.0", "@typescript-eslint/eslint-plugin@^5.31.0", "@typescript-eslint/eslint-plugin@^5.5.0": +"@typescript-eslint/eslint-plugin@^5.23.0", "@typescript-eslint/eslint-plugin@^5.27.0", "@typescript-eslint/eslint-plugin@^5.31.0": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.1.tgz#696b9cc21dfd4749c1c8ad1307f76a36a00aa0e3" integrity sha512-LyR6x784JCiJ1j6sH5Y0K6cdExqCCm8DJUTcwG5ThNXJj/G8o5E56u5EdG4SLy+bZAwZBswC+GYn3eGdttBVCg== @@ -5692,14 +5789,29 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/eslint-plugin@^5.5.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz#ffa505cf961d4844d38cfa19dcec4973a6039e41" + integrity sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA== + dependencies: + "@typescript-eslint/scope-manager" "5.45.0" + "@typescript-eslint/type-utils" "5.45.0" + "@typescript-eslint/utils" "5.45.0" + debug "^4.3.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/experimental-utils@^5.0.0": - version "5.42.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.42.1.tgz#755695d8f1b45dff7b5626cb48f33542227a3471" - integrity sha512-qona75z2MLpeZADEuCet5Pwvh1g/0cWScEEDy43chuUPc4klgDiwz5hLFk5dHcjFEETSYQHRPYiiHKW24EMPjw== + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.45.0.tgz#b59fea61a855cb2b1fdbd0fb45934a844f4bf870" + integrity sha512-DnRQg5+3uHHt/gaifTjwg9OKbg9/TWehfJzYHQIDJboPEbF897BKDE/qoqMhW7nf0jWRV1mwVXTaUvtB1/9Gwg== dependencies: - "@typescript-eslint/utils" "5.42.1" + "@typescript-eslint/utils" "5.45.0" -"@typescript-eslint/parser@^5.23.0", "@typescript-eslint/parser@^5.27.0", "@typescript-eslint/parser@^5.31.0", "@typescript-eslint/parser@^5.5.0": +"@typescript-eslint/parser@^5.23.0", "@typescript-eslint/parser@^5.27.0", "@typescript-eslint/parser@^5.31.0": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.42.1.tgz#3e66156f2f74b11690b45950d8f5f28a62751d35" integrity sha512-kAV+NiNBWVQDY9gDJDToTE/NO8BHi4f6b7zTsVAJoTkmB/zlfOpiEVBzHOKtlgTndCKe8vj9F/PuolemZSh50Q== @@ -5709,6 +5821,16 @@ "@typescript-eslint/typescript-estree" "5.42.1" debug "^4.3.4" +"@typescript-eslint/parser@^5.5.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.0.tgz#b18a5f6b3cf1c2b3e399e9d2df4be40d6b0ddd0e" + integrity sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ== + dependencies: + "@typescript-eslint/scope-manager" "5.45.0" + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/typescript-estree" "5.45.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.42.1": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.42.1.tgz#05e5e1351485637d466464237e5259b49f609b18" @@ -5717,6 +5839,14 @@ "@typescript-eslint/types" "5.42.1" "@typescript-eslint/visitor-keys" "5.42.1" +"@typescript-eslint/scope-manager@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz#7a4ac1bfa9544bff3f620ab85947945938319a96" + integrity sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw== + dependencies: + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/visitor-keys" "5.45.0" + "@typescript-eslint/type-utils@5.42.1": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.42.1.tgz#21328feb2d4b193c5852b35aabd241ccc1449daa" @@ -5727,11 +5857,26 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz#aefbc954c40878fcebeabfb77d20d84a3da3a8b2" + integrity sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q== + dependencies: + "@typescript-eslint/typescript-estree" "5.45.0" + "@typescript-eslint/utils" "5.45.0" + debug "^4.3.4" + tsutils "^3.21.0" + "@typescript-eslint/types@5.42.1": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.42.1.tgz#0d4283c30e9b70d2aa2391c36294413de9106df2" integrity sha512-Qrco9dsFF5lhalz+lLFtxs3ui1/YfC6NdXu+RAGBa8uSfn01cjO7ssCsjIsUs484vny9Xm699FSKwpkCcqwWwA== +"@typescript-eslint/types@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.0.tgz#794760b9037ee4154c09549ef5a96599621109c5" + integrity sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA== + "@typescript-eslint/typescript-estree@5.42.1": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.1.tgz#f9a223ecb547a781d37e07a5ac6ba9ff681eaef0" @@ -5745,7 +5890,20 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.42.1", "@typescript-eslint/utils@^5.13.0": +"@typescript-eslint/typescript-estree@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz#f70a0d646d7f38c0dfd6936a5e171a77f1e5291d" + integrity sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ== + dependencies: + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/visitor-keys" "5.45.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.42.1": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.42.1.tgz#2789b1cd990f0c07aaa3e462dbe0f18d736d5071" integrity sha512-Gxvf12xSp3iYZd/fLqiQRD4uKZjDNR01bQ+j8zvhPjpsZ4HmvEFL/tC4amGNyxN9Rq+iqvpHLhlqx6KTxz9ZyQ== @@ -5759,6 +5917,20 @@ eslint-utils "^3.0.0" semver "^7.3.7" +"@typescript-eslint/utils@5.45.0", "@typescript-eslint/utils@^5.13.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.45.0.tgz#9cca2996eee1b8615485a6918a5c763629c7acf5" + integrity sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.45.0" + "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/typescript-estree" "5.45.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@5.42.1": version "5.42.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.1.tgz#df10839adf6605e1cdb79174cf21e46df9be4872" @@ -5767,6 +5939,14 @@ "@typescript-eslint/types" "5.42.1" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.45.0": + version "5.45.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz#e0d160e9e7fdb7f8da697a5b78e7a14a22a70528" + integrity sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg== + dependencies: + "@typescript-eslint/types" "5.45.0" + eslint-visitor-keys "^3.3.0" + "@wagmi/core@^0.6.12": version "0.6.12" resolved "https://registry.yarnpkg.com/@wagmi/core/-/core-0.6.12.tgz#19d35424840a8c9f5396869e24464e2137ea8fc2" @@ -6352,7 +6532,7 @@ ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@~6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.11.0, ajv@^8.6.0, ajv@^8.8.0: +ajv@^8.0.0, ajv@^8.11.0, ajv@^8.8.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== @@ -6362,6 +6542,16 @@ ajv@^8.0.0, ajv@^8.11.0, ajv@^8.6.0, ajv@^8.8.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.6.0: + version "8.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.2.tgz#aecb20b50607acf2569b6382167b65a96008bb78" + integrity sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-align@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -6572,7 +6762,7 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== -array-includes@^3.1.4, array-includes@^3.1.5: +array-includes@^3.1.4, array-includes@^3.1.5, array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== @@ -6615,7 +6805,7 @@ array.prototype.flat@^1.2.3, array.prototype.flat@^1.2.5: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.0: +array.prototype.flatmap@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== @@ -6636,6 +6826,17 @@ array.prototype.reduce@^1.0.5: es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -6739,7 +6940,7 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^10.4.11: +autoprefixer@^10.4.13: version "10.4.13" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== @@ -6783,9 +6984,9 @@ aws4@^1.8.0: integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== axe-core@^4.4.3: - version "4.5.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.5.1.tgz#04d561c11b6d76d096d34e9d14ba2c294fb20cdc" - integrity sha512-1exVbW0X1O/HSr/WMwnaweyqcWOgZgLiVxdLG34pvSQk4NlYQr9OUy0JLwuhFfuVNQzzqgH57eYzkFBCb3bIsQ== + version "4.5.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.5.2.tgz#823fdf491ff717ac3c58a52631d4206930c1d9f7" + integrity sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA== axios@^0.21.0, axios@^0.21.1: version "0.21.4" @@ -6802,7 +7003,7 @@ axios@^0.27.2: follow-redirects "^1.14.9" form-data "^4.0.0" -axios@^1.0.0, axios@^1.1.3: +axios@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== @@ -7635,7 +7836,12 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001426: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001426: + version "1.0.30001434" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz#ec1ec1cfb0a93a34a0600d37903853030520a4e5" + integrity sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA== + +caniuse-lite@^1.0.30001400: version "1.0.30001431" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795" integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ== @@ -7714,9 +7920,9 @@ chardet@^0.7.0: integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== check-types@^11.1.1: - version "11.1.2" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f" - integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== + version "11.2.2" + resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.2.tgz#7afc0b6a860d686885062f2dba888ba5710335b4" + integrity sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA== checkpoint-store@^1.1.0: version "1.1.0" @@ -8414,14 +8620,14 @@ core-js-compat@^3.25.1: browserslist "^4.21.4" core-js-pure@^3.23.3, core-js-pure@^3.25.1: - version "3.26.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.26.0.tgz#7ad8a5dd7d910756f3124374b50026e23265ca9a" - integrity sha512-LiN6fylpVBVwT8twhhluD9TzXmZQQsr2I2eIKtWNbZI1XMfBT7CV18itaN6RA7EtQd/SDdRx/wzvAShX2HvhQA== + version "3.26.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.26.1.tgz#653f4d7130c427820dcecd3168b594e8bb095a33" + integrity sha512-VVXcDpp/xJ21KdULRq/lXdLzQAtX7+37LzpyfFM973il0tWSsDEoyzG38G14AjTpK9VTfiNM9jnFauq/CpaWGQ== core-js@^3.19.2: - version "3.26.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.0.tgz#a516db0ed0811be10eac5d94f3b8463d03faccfe" - integrity sha512-+DkDrhoR4Y0PxDz6rurahuB+I45OsEUv8E1maPTB6OuHRohMMcznBq9TMpdpDMm/hUPob/mJJS3PqgbHpMTQgw== + version "3.26.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e" + integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA== core-util-is@1.0.2: version "1.0.2" @@ -8625,18 +8831,18 @@ css-has-pseudo@^3.0.4: postcss-selector-parser "^6.0.9" css-loader@^6.5.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e" - integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw== + version "6.7.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.2.tgz#26bc22401b5921686a10fbeba75d124228302304" + integrity sha512-oqGbbVcBJkm8QwmnNzrFrWTnudnRZC+1eXikLJl0n4ljcfotgRifpg2a1lKy8jTrc4/d9A/ap1GFq1jDKG7J+Q== dependencies: icss-utils "^5.1.0" - postcss "^8.4.7" + postcss "^8.4.18" postcss-modules-extract-imports "^3.0.0" postcss-modules-local-by-default "^4.0.0" postcss-modules-scope "^3.0.0" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.3.5" + semver "^7.3.8" css-minimizer-webpack-plugin@^3.2.0: version "3.4.1" @@ -8736,7 +8942,7 @@ css.escape@^1.5.1: resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== -cssdb@^7.0.1: +cssdb@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.1.0.tgz#574f97235a83eb753a29f0b1f2cbacac0d628bb8" integrity sha512-Sd99PrFgx28ez4GHu8yoQIufc/70h9oYowDf4EjeIKi8mac9whxRjhM3IaMr6EllP6KKKWtJrMfN6C7T9tIWvQ== @@ -10162,24 +10368,25 @@ eslint-plugin-react-hooks@^4.3.0: integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== eslint-plugin-react@^7.27.1: - version "7.31.10" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz#6782c2c7fe91c09e715d536067644bbb9491419a" - integrity sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA== + version "7.31.11" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz#011521d2b16dcf95795df688a4770b4eaab364c8" + integrity sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw== dependencies: - array-includes "^3.1.5" - array.prototype.flatmap "^1.3.0" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.1" - object.values "^1.1.5" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" prop-types "^15.8.1" resolve "^2.0.0-next.3" semver "^6.3.0" - string.prototype.matchall "^4.0.7" + string.prototype.matchall "^4.0.8" eslint-plugin-testing-library@^5.0.1: version "5.9.1" @@ -10232,7 +10439,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.15.0, eslint@^8.3.0: +eslint@^8.15.0: version "8.27.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.27.0.tgz#d547e2f7239994ad1faa4bb5d84e5d809db7cf64" integrity sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ== @@ -10321,6 +10528,51 @@ eslint@^8.16.0, eslint@^8.20.0: strip-json-comments "^3.1.0" text-table "^0.2.0" +eslint@^8.28.0, eslint@^8.3.0: + version "8.28.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.28.0.tgz#81a680732634677cc890134bcdd9fdfea8e63d6e" + integrity sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ== + dependencies: + "@eslint/eslintrc" "^1.3.3" + "@humanwhocodes/config-array" "^0.11.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.15.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + esniff@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/esniff/-/esniff-1.1.0.tgz#c66849229f91464dede2e0d40201ed6abf65f2ac" @@ -14627,19 +14879,19 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@^2.0.0, loader-utils@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.3.tgz#d4b15b8504c63d1fc3f2ade52d41bc8459d6ede1" - integrity sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A== +loader-utils@^2.0.0, loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" json5 "^2.1.2" loader-utils@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" - integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== + version "3.2.1" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.1.tgz#4fb104b599daafd82ef3e1a41fb9265f87e1f576" + integrity sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw== locate-path@^2.0.0: version "2.0.0" @@ -15132,9 +15384,9 @@ memdown@^1.0.0: safe-buffer "~5.1.1" memfs@^3.1.2, memfs@^3.4.3: - version "3.4.10" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.10.tgz#4cdff7cfd21351a85e11b08aa276ebf100210a4d" - integrity sha512-0bCUP+L79P4am30yP1msPzApwuMQG23TjwlwdHeEV5MxioDR1a0AgB0T9FfggU52eJuDCq8WVwb5ekznFyWiTQ== + version "3.4.12" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.12.tgz#d00f8ad8dab132dc277c659dc85bfd14b07d03bd" + integrity sha512-BcjuQn6vfqP+k100e0E9m61Hyqa//Brp+I3f0OBmN0ATHlFA8vx3Lt8z57R3u2bPqe3WGDBC+nF72fTH7isyEw== dependencies: fs-monkey "^1.0.3" @@ -15302,9 +15554,9 @@ min-indent@^1.0.0: integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== mini-css-extract-plugin@^2.4.5: - version "2.6.1" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz#9a1251d15f2035c342d99a468ab9da7a0451b71e" - integrity sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg== + version "2.7.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.0.tgz#d7d9ba0c5b596d155e36e2b174082fc7f010dd64" + integrity sha512-auqtVo8KhTScMsba7MbijqZTfibbXiBNlPAQbsVt7enQfcDYLdgG57eGxMqwVU3mfeWANY4F1wUg+rMF+ycZgw== dependencies: schema-utils "^4.0.0" @@ -16155,7 +16407,7 @@ object.assign@^4.1.2, object.assign@^4.1.3, object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.5: +object.entries@^1.1.5, object.entries@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== @@ -16164,7 +16416,7 @@ object.entries@^1.1.5: define-properties "^1.1.4" es-abstract "^1.20.4" -object.fromentries@^2.0.5: +object.fromentries@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== @@ -16183,7 +16435,7 @@ object.getownpropertydescriptors@^2.1.0: define-properties "^1.1.4" es-abstract "^1.20.4" -object.hasown@^1.1.1, object.hasown@^1.1.2: +object.hasown@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== @@ -16198,7 +16450,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.5: +object.values@^1.1.0, object.values@^1.1.5, object.values@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== @@ -16897,7 +17149,7 @@ postcss-custom-media@^8.0.2: dependencies: postcss-value-parser "^4.2.0" -postcss-custom-properties@^12.1.9: +postcss-custom-properties@^12.1.10: version "12.1.10" resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-12.1.10.tgz#624517179fd4cf50078a7a60f628d5782e7d4903" integrity sha512-U3BHdgrYhCrwTVcByFHs9EOBoqcKq4Lf3kXwbTi4hhq0qWhl/pDWq2THbv/ICX/Fl9KqeHBb8OVrTf2OaYF07A== @@ -17243,11 +17495,11 @@ postcss-place@^7.0.5: postcss-value-parser "^4.2.0" postcss-preset-env@^7.0.1: - version "7.8.2" - resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.8.2.tgz#4c834d5cbd2e29df2abf59118947c456922b79ba" - integrity sha512-rSMUEaOCnovKnwc5LvBDHUDzpGP+nrUeWZGWt9M72fBvckCi45JmnJigUr4QG4zZeOHmOCNCZnd2LKDvP++ZuQ== + version "7.8.3" + resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz#2a50f5e612c3149cc7af75634e202a5b2ad4f1e2" + integrity sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag== dependencies: - "@csstools/postcss-cascade-layers" "^1.1.0" + "@csstools/postcss-cascade-layers" "^1.1.1" "@csstools/postcss-color-function" "^1.1.1" "@csstools/postcss-font-format-keywords" "^1.0.1" "@csstools/postcss-hwb-function" "^1.0.2" @@ -17261,19 +17513,19 @@ postcss-preset-env@^7.0.1: "@csstools/postcss-text-decoration-shorthand" "^1.0.0" "@csstools/postcss-trigonometric-functions" "^1.0.2" "@csstools/postcss-unset-value" "^1.0.2" - autoprefixer "^10.4.11" - browserslist "^4.21.3" + autoprefixer "^10.4.13" + browserslist "^4.21.4" css-blank-pseudo "^3.0.3" css-has-pseudo "^3.0.4" css-prefers-color-scheme "^6.0.3" - cssdb "^7.0.1" + cssdb "^7.1.0" postcss-attribute-case-insensitive "^5.0.2" postcss-clamp "^4.1.0" postcss-color-functional-notation "^4.2.4" postcss-color-hex-alpha "^8.0.4" postcss-color-rebeccapurple "^7.1.1" postcss-custom-media "^8.0.2" - postcss-custom-properties "^12.1.9" + postcss-custom-properties "^12.1.10" postcss-custom-selectors "^6.0.3" postcss-dir-pseudo-class "^6.0.5" postcss-double-position-gradients "^3.1.2" @@ -17332,9 +17584,9 @@ postcss-selector-not@^6.0.1: postcss-selector-parser "^6.0.10" postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + version "6.0.11" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc" + integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -17367,10 +17619,10 @@ postcss@^7.0.35: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.4, postcss@^8.4.7: - version "8.4.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.18.tgz#6d50046ea7d3d66a85e0e782074e7203bc7fbca2" - integrity sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA== +postcss@^8.3.5, postcss@^8.4.18, postcss@^8.4.4: + version "8.4.19" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.19.tgz#61178e2add236b17351897c8bcc0b4c8ecab56fc" + integrity sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA== dependencies: nanoid "^3.3.4" picocolors "^1.0.0" @@ -18150,11 +18402,16 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.9: +regenerator-runtime@^0.13.10: version "0.13.10" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz#ed07b19616bcbec5da6274ebc75ae95634bfc2ee" integrity sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw== +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.9: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regenerator-transform@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" @@ -19517,7 +19774,7 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7: +string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== @@ -19847,9 +20104,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== tailwindcss@^3.0.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.3.tgz#c5ee3cb95dae7a80592d43a460d277915c7b2938" - integrity sha512-Xt9D4PK4zuuQCEB8bwK9JUCKmTgUwyac/6b0/42Vqhgl6YJkep+Wf5wq+5uXYfmrupdAD0YY2NY1hyZp1HjRrg== + version "3.2.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.4.tgz#afe3477e7a19f3ceafb48e4b083e292ce0dc0250" + integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ== dependencies: arg "^5.0.2" chokidar "^3.5.3" @@ -19979,7 +20236,17 @@ terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.5: serialize-javascript "^6.0.0" terser "^5.14.1" -terser@^5.0.0, terser@^5.10.0, terser@^5.14.1: +terser@^5.0.0, terser@^5.10.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.0.tgz#29362c6f5506e71545c73b069ccd199bb28f7f54" + integrity sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +terser@^5.14.1: version "5.15.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c" integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==