diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000000..d94b889f8dc --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,195 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + }, + extends: [ + 'eslint:recommended', + 'standard', + 'plugin:import/errors', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/stylistic', + 'plugin:@typescript-eslint/recommended', + 'prettier', + ], + plugins: [ + '@stylistic', + '@typescript-eslint', + 'no-relative-import-paths', + 'unused-imports', + 'import', + 'jsdoc', + ], + env: { + es6: true, + node: true, + }, + ignorePatterns: [ + 'dist', + 'node_modules', + '.eslintrc.*', + 'rollup', + 'rollup.config.*', + 'setupTests.ts', + 'jest.setup.*', + 'jest.config.*', + // temporarily disable lint on __tests__ + '__tests__', + ], + rules: { + camelcase: [ + 'error', + { + allow: [ + 'graphql_headers', + // exceptions for the legacy config + /^(aws_|amazon_)/, + 'access_key', + 'secret_key', + 'session_token', + // exceptions for the auth package + 'redirect_uri', + 'response_type', + 'client_id', + 'identity_provider', + 'code_challenge', + 'code_challenge_method', + 'grant_type', + 'code_verifier', + 'logout_uri', + 'id_token', + 'access_token', + 'token_type', + 'expires_in', + 'error_description', + // exceptions for the notifications package + 'campaign_id', + 'delivery_type', + 'treatment_id', + 'campaign_activity_id', + ], + }, + ], + 'import/no-deprecated': 'warn', + 'import/no-empty-named-blocks': 'error', + 'import/no-mutable-exports': 'error', + 'import/no-relative-packages': 'error', + 'import/newline-after-import': 'error', + 'import/order': [ + 'error', + { + groups: [ + 'builtin', + ['external', 'internal', 'parent'], + 'sibling', + 'index', + 'object', + 'type', + ], + 'newlines-between': 'always', + pathGroups: [ + { + pattern: '~/**', + group: 'parent', + }, + ], + }, + ], + 'no-eval': 'error', + 'no-param-reassign': 'error', + 'no-shadow': 'off', + 'no-use-before-define': 'off', + 'no-useless-constructor': 'off', + 'no-trailing-spaces': 'error', + 'no-return-await': 'error', + 'object-shorthand': 'error', + 'prefer-destructuring': 'off', + 'promise/catch-or-return': [ + 'error', + { terminationMethod: ['then', 'catch', 'asCallback', 'finally'] }, + ], + 'space-before-function-paren': 'off', + 'sort-imports': ['error', { ignoreDeclarationSort: true }], + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'error', + { + vars: 'all', + varsIgnorePattern: '^_', + args: 'after-used', + argsIgnorePattern: '^_', + }, + ], + 'valid-typeof': ['error', { requireStringLiterals: false }], + '@stylistic/comma-dangle': [ + 'error', + { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + functions: 'always-multiline', + enums: 'always-multiline', + generics: 'always-multiline', + tuples: 'always-multiline', + }, + ], + '@stylistic/function-call-argument-newline': ['error', 'consistent'], + '@stylistic/indent': 'off', + '@stylistic/max-len': [ + 'error', + { + code: 120, + ignoreComments: true, + ignoreUrls: true, + ignoreStrings: true, + ignoreTemplateLiterals: true, + ignoreRegExpLiterals: true, + }, + ], + '@stylistic/padding-line-between-statements': [ + 'error', + { blankLine: 'always', prev: '*', next: 'return' }, + ], + '@typescript-eslint/method-signature-style': ['error', 'method'], + '@typescript-eslint/no-confusing-void-expression': 'error', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-namespace': ['error', { allowDeclarations: true }], + '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-use-before-define': [ + 'error', + { functions: false, variables: false, classes: false }, + ], + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/prefer-destructuring': [ + 'error', + { object: true, array: false }, + ], + '@typescript-eslint/space-before-function-paren': [ + 'error', + { + anonymous: 'never', + named: 'never', + asyncArrow: 'always', + }, + ], + 'jsdoc/no-undefined-types': 1, + }, + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + alwaysTryTypes: true, + project: ['packages/*/tsconfig.json', 'tsconfig.json'], + }, + }, + 'import/ignore': ['react-native'], + }, +}; diff --git a/.eslintrc.scoped.js b/.eslintrc.scoped.js new file mode 100644 index 00000000000..a37ec028dd8 --- /dev/null +++ b/.eslintrc.scoped.js @@ -0,0 +1,18 @@ +// This eslintrc is used when running `eslint src` (yarn lint) in the scope of each package in the lerna workspace +// so that the no-relative-import-paths plugin can correctly apply rule checks + +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + // relative to the root of a package itself + rootDir: '.', + }, + ], + }, +}; diff --git a/.prettierignore b/.prettierignore index e92f704c401..058540b46e3 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,7 +5,6 @@ docs package.json yarn.lock package-lock.json -.eslintrc.js www .stencil PULL_REQUEST_TEMPLATE.md diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 12cb1778d76..d7e6315c08b 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,11 +1,7 @@ { - // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - // List of extensions which should be recommended for users of this workspace. "recommendations": [ "esbenp.prettier-vscode", - "tombonnike.vscode-status-bar-format-toggle" + "dbaeumer.vscode-eslint", + "streetsidesoftware.code-spell-checker" ], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 7600d0c25b3..b4ede55a130 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,12 +1,13 @@ { - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.detectIndentation": false, - "editor.formatOnSave": true, - "editor.insertSpaces": false, - "editor.tabSize": 4, - "prettier.requireConfig": true, - "typescript.tsdk": "node_modules/typescript/lib" + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.detectIndentation": false, + "editor.formatOnSave": true, + "editor.insertSpaces": false, + "editor.tabSize": 4, + "prettier.requireConfig": true, + "typescript.preferences.importModuleSpecifier": "non-relative", + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index cf70f54ed17..00000000000 --- a/jest.config.js +++ /dev/null @@ -1,25 +0,0 @@ -/** @type {import('jest').Config} */ -module.exports = { - workerIdleMemoryLimit: '512MB', - coveragePathIgnorePatterns: [ - '/node_modules/', - 'dist', - '__tests__', - ], - setupFiles: ['../../jest.setup.js'], - testEnvironment: 'jsdom', - testRegex: '/__tests__/.*\\.(test|spec)\\.[jt]sx?$', - transform: { - '^.+\\.(js|jsx|ts|tsx)$': [ - 'ts-jest', - { - tsconfig: { - allowJs: true, - lib: ['dom', 'es2020'], - noImplicitAny: false, - types: ['jest', 'jsdom'], - }, - }, - ], - }, -}; diff --git a/jest/getJestConfig.mjs b/jest/getJestConfig.mjs new file mode 100644 index 00000000000..5ec69084d28 --- /dev/null +++ b/jest/getJestConfig.mjs @@ -0,0 +1,36 @@ +import typescript from 'typescript'; +import * as tsJest from 'ts-jest'; +import deepmerge from 'deepmerge'; + +/** + * Creates a jest config object by merging typescript compiler options and jest config object into the base jest config. + * + * @param {typescript.CompilerOptions} tsCompilerOptions The compiler options of `tsconfig`. + * @param {tsJest.JestConfigWithTsJest?} jestConfig The jest config to be merged into the base jest config. + * @return {tsJest.JestConfigWithTsJest} The jest config object. + */ +export const getJestConfig = (tsCompilerOptions, jestConfig = {}) => + deepmerge( + { + workerIdleMemoryLimit: '512MB', + coveragePathIgnorePatterns: ['/node_modules/', 'dist', '__tests__'], + setupFiles: ['../../jest.setup.js'], + testEnvironment: 'jsdom', + testRegex: '/__tests__/.*\\.(test|spec)\\.[jt]sx?$', + transform: { + '^.+\\.(js|jsx|ts|tsx)$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.test.json', + }, + ], + }, + moduleNameMapper: tsJest.pathsToModuleNameMapper( + tsCompilerOptions.paths, + { + prefix: '/', + }, + ), + }, + jestConfig, + ); diff --git a/jest/requireResolve.mjs b/jest/requireResolve.mjs new file mode 100644 index 00000000000..8a6572eec28 --- /dev/null +++ b/jest/requireResolve.mjs @@ -0,0 +1,11 @@ +import { createRequire } from 'module'; + +const require = createRequire(import.meta.url); + +/** + * Resolves module path name. + * + * @param {String} moduleName Module name. + * @returns {String} Module path name; + */ +export const requireResolve = moduleName => require.resolve(moduleName); diff --git a/license_config.json b/license_config.json index f0bac69b3a4..40aafcf2a65 100644 --- a/license_config.json +++ b/license_config.json @@ -38,7 +38,9 @@ "**/Gemfile", "**/.rollup.cache", "**/rollup.config.mjs", - "rollup" + "rollup", + "jest", + "**/jest.config.mjs" ], "ignoreFile": ".gitignore", "license": "license_header.txt", diff --git a/package.json b/package.json index 5488455c90f..1d32a2f31f1 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "clean:size": "lerna run clean:size --parallel", "format": "lerna run format", "lint": "lerna run lint", + "lint:fix": "lerna run lint:fix", "lint:license": "license-check-and-add add -f license_config.json", "link-all": "yarn unlink-all && lerna exec --no-bail --parallel yarn link", "unlink-all": "lerna exec --no-bail --parallel -- yarn unlink; exit 0", @@ -86,12 +87,28 @@ "@size-limit/file": "^8.1.0", "@size-limit/webpack": "^8.1.0", "@size-limit/webpack-why": "^8.1.0", + "@stylistic/eslint-plugin": "^1.4.0", "@types/jest": "^29.5.8", "@types/lodash": "4.14.182", "@types/node": "^8.9.5", "@types/puppeteer": "1.3.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", + "babel-jest": "^24.9.0", "babel-loader": "^8.3.0", "codecov": "^3.6.5", + "cross-env": "^7.0.3", + "deepmerge": "^4.3.1", + "eslint": "^8.53.0", + "eslint-config-prettier": "^9.0.0", + "eslint-config-standard": "^17.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jsdoc": "^46.9.0", + "eslint-plugin-n": "^16.3.1", + "eslint-plugin-no-relative-import-paths": "^1.5.3", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-unused-imports": "^3.0.0", "glob": "^10.3.10", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -110,11 +127,11 @@ "terser-webpack-plugin": "^5.3.6", "ts-jest": "^29.1.1", "ts-loader": "^9.4.3", - "tslint": "^5.7.0", - "tslint-config-airbnb": "^5.8.0", + "ts-patch": "^3.0.2", "typedoc": "^0.17.0", - "typescript": "^4.3.5", + "typescript": "~5.0.2", "typescript-coverage-report": "^0.6.4", + "typescript-transform-paths": "^3.4.6", "uuid-validate": "^0.0.3", "webpack": "^5.75.0", "webpack-bundle-analyzer": "^4.7.0", diff --git a/packages/adapter-nextjs/.eslintrc.js b/packages/adapter-nextjs/.eslintrc.js new file mode 100644 index 00000000000..a01fa4a7cba --- /dev/null +++ b/packages/adapter-nextjs/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/adapter-nextjs', + }, + ], + }, +}; diff --git a/packages/adapter-nextjs/jest.config.js b/packages/adapter-nextjs/jest.config.js deleted file mode 100644 index c6406b36105..00000000000 --- a/packages/adapter-nextjs/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 88, - functions: 90, - lines: 92, - statements: 93, - }, - }, -}; diff --git a/packages/adapter-nextjs/jest.config.mjs b/packages/adapter-nextjs/jest.config.mjs new file mode 100644 index 00000000000..7ac5eb9858b --- /dev/null +++ b/packages/adapter-nextjs/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 88, + functions: 90, + lines: 92, + statements: 93, + }, + }, +}); + +export default jestConfig; diff --git a/packages/adapter-nextjs/package.json b/packages/adapter-nextjs/package.json index 04244d4ed0a..bb4c03487a6 100644 --- a/packages/adapter-nextjs/package.json +++ b/packages/adapter-nextjs/package.json @@ -19,8 +19,7 @@ "aws-amplify": "6.0.8", "jest-fetch-mock": "3.0.3", "next": ">= 13.5.0 < 15.0.0", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "publishConfig": { "access": "public" @@ -62,14 +61,15 @@ "scripts": { "build": "npm run clean && npm run build:esm-cjs", "build-with-test": "npm test && npm run build", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "clean": "npm run clean:size && rimraf dist", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.build.json -t 90.31" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 90.31" } } diff --git a/packages/adapter-nextjs/src/api/createServerRunnerForAPI.ts b/packages/adapter-nextjs/src/api/createServerRunnerForAPI.ts index e9e7c8df222..202f10d37ca 100644 --- a/packages/adapter-nextjs/src/api/createServerRunnerForAPI.ts +++ b/packages/adapter-nextjs/src/api/createServerRunnerForAPI.ts @@ -2,8 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { ResourcesConfig } from '@aws-amplify/core'; -import { createRunWithAmplifyServerContext, getAmplifyConfig } from '../utils'; -import { NextServer } from '../types'; +import { + createRunWithAmplifyServerContext, + getAmplifyConfig, +} from '~/src/utils'; +import { NextServer } from '~/src/types'; export const createServerRunnerForAPI = ({ config, diff --git a/packages/adapter-nextjs/src/api/generateServerClient.ts b/packages/adapter-nextjs/src/api/generateServerClient.ts index 66c92699380..b6e8b5c9853 100644 --- a/packages/adapter-nextjs/src/api/generateServerClient.ts +++ b/packages/adapter-nextjs/src/api/generateServerClient.ts @@ -4,31 +4,31 @@ import { generateClientWithAmplifyInstance } from '@aws-amplify/api/internals'; import { generateClient } from 'aws-amplify/api/server'; import { - getAmplifyServerContext, AmplifyServerContextError, + getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; import { - V6ClientSSRRequest, V6ClientSSRCookies, - __amplify, + V6ClientSSRRequest, } from '@aws-amplify/api-graphql'; -import { NextServer } from '../types'; -import { createServerRunnerForAPI } from './createServerRunnerForAPI'; -import { getAmplifyConfig } from '../utils'; import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils'; +import { NextServer } from '~/src/types'; +import { getAmplifyConfig } from '~/src/utils'; -type CookiesClientParams = { +import { createServerRunnerForAPI } from './createServerRunnerForAPI'; + +interface CookiesClientParams { cookies: NextServer.ServerComponentContext['cookies']; config: NextServer.CreateServerRunnerInput['config']; authMode?: GraphQLAuthMode; authToken?: string; -}; +} -type ReqClientParams = { +interface ReqClientParams { config: NextServer.CreateServerRunnerInput['config']; authMode?: GraphQLAuthMode; authToken?: string; -}; +} /** * Generates an API client that can be used inside a Next.js Server Component with Dynamic Rendering @@ -98,6 +98,7 @@ export function generateServerClientUsingReqRes< T extends Record = never, >({ config, authMode, authToken }: ReqClientParams): V6ClientSSRRequest { const amplifyConfig = getAmplifyConfig(config); + return generateClient({ config: amplifyConfig, authMode, diff --git a/packages/adapter-nextjs/src/api/index.ts b/packages/adapter-nextjs/src/api/index.ts index a7b0695b59f..6ac4837d7c3 100644 --- a/packages/adapter-nextjs/src/api/index.ts +++ b/packages/adapter-nextjs/src/api/index.ts @@ -1,16 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export { - generateServerClientUsingReqRes, - generateServerClientUsingCookies, -} from './generateServerClient'; - import { V6ClientSSRCookies, V6ClientSSRRequest, } from '@aws-amplify/api-graphql'; +export { + generateServerClientUsingReqRes, + generateServerClientUsingCookies, +} from './generateServerClient'; + // explicitly defaulting to `never` here resolves // TS2589: Type instantiation is excessively deep and possibly infinite. // When this type is used without a generic type arg, i.e. `let client: Client` diff --git a/packages/adapter-nextjs/src/createServerRunner.ts b/packages/adapter-nextjs/src/createServerRunner.ts index 76d238cdd15..3887054c4f7 100644 --- a/packages/adapter-nextjs/src/createServerRunner.ts +++ b/packages/adapter-nextjs/src/createServerRunner.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { ResourcesConfig } from 'aws-amplify'; + import { createRunWithAmplifyServerContext, getAmplifyConfig } from './utils'; import { NextServer } from './types'; diff --git a/packages/adapter-nextjs/src/types/NextServer.ts b/packages/adapter-nextjs/src/types/NextServer.ts index a727204bc00..59cbb8d3b4d 100644 --- a/packages/adapter-nextjs/src/types/NextServer.ts +++ b/packages/adapter-nextjs/src/types/NextServer.ts @@ -8,17 +8,17 @@ import { LegacyConfig } from 'aws-amplify/adapter-core'; import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core'; import { ResourcesConfig } from '@aws-amplify/core'; -export namespace NextServer { +export declare namespace NextServer { /** * This context is normally available in the following: * - Next App Router [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) * - Next App Router [route handler](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) * when using `NextResponse` to create the response of the route handler */ - export type NextRequestAndNextResponseContext = { + export interface NextRequestAndNextResponseContext { request: NextRequest; response: NextResponse; - }; + } /** * This context is normally available in the following: @@ -26,10 +26,10 @@ export namespace NextServer { * when using the Web API [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) * to create the response of the route handler */ - export type NextRequestAndResponseContext = { + export interface NextRequestAndResponseContext { request: NextRequest; response: Response; - }; + } /** * This context is normally available in the following: @@ -37,9 +37,9 @@ export namespace NextServer { * where the [`cookies`](https://nextjs.org/docs/app/api-reference/functions/cookies) * function can be imported and called */ - export type ServerComponentContext = { + export interface ServerComponentContext { cookies: typeof cookies; - }; + } export type ServerActionContext = ServerComponentContext; @@ -48,10 +48,10 @@ export namespace NextServer { * [`getServerSideProps`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props) * function of the Next Pages Router. */ - export type GetServerSidePropsContext = { + export interface GetServerSidePropsContext { request: NextGetServerSidePropsContext['req']; response: NextGetServerSidePropsContext['res']; - }; + } /** * The union of possible Next.js app server context types. @@ -63,20 +63,16 @@ export namespace NextServer { | GetServerSidePropsContext; /** - * The interface of the input of {@link RunOperationWithContext}. + * The interface of the input to run an operation in a server context. */ export interface RunWithContextInput { nextServerContext: Context | null; - operation: ( - contextSpec: AmplifyServer.ContextSpec - ) => OperationResult | Promise; + operation(contextSpec: AmplifyServer.ContextSpec): OperationResult | Promise; } - export interface RunOperationWithContext { - ( - input: RunWithContextInput - ): Promise; - } + export type RunOperationWithContext = ( + input: RunWithContextInput + ) => Promise; export interface CreateServerRunnerInput { config: ResourcesConfig | LegacyConfig; @@ -86,7 +82,5 @@ export namespace NextServer { runWithAmplifyServerContext: RunOperationWithContext; } - export interface CreateServerRunner { - (input: CreateServerRunnerInput): CreateServerRunnerOutput; - } + export type CreateServerRunner = (input: CreateServerRunnerInput) => CreateServerRunnerOutput; } diff --git a/packages/adapter-nextjs/src/utils/createCookieStorageAdapterFromNextServerContext.ts b/packages/adapter-nextjs/src/utils/createCookieStorageAdapterFromNextServerContext.ts index beffc7ae31e..1de0efe87f5 100644 --- a/packages/adapter-nextjs/src/utils/createCookieStorageAdapterFromNextServerContext.ts +++ b/packages/adapter-nextjs/src/utils/createCookieStorageAdapterFromNextServerContext.ts @@ -2,16 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import { NextRequest, NextResponse } from 'next/server.js'; -import { NextServer } from '../types'; import { AmplifyServerContextError, CookieStorage, } from '@aws-amplify/core/internals/adapter-core'; +import { NextServer } from '~/src/types'; export const DATE_IN_THE_PAST = new Date(0); export const createCookieStorageAdapterFromNextServerContext = ( - context: NextServer.Context + context: NextServer.Context, ): CookieStorage.Adapter => { const { request: req, response: res } = context as Partial; @@ -45,12 +45,12 @@ export const createCookieStorageAdapterFromNextServerContext = ( if (response instanceof NextResponse) { return createCookieStorageAdapterFromNextRequestAndNextResponse( request, - response + response, ); } else { return createCookieStorageAdapterFromNextRequestAndHttpResponse( request, - response + response, ); } } @@ -72,7 +72,7 @@ export const createCookieStorageAdapterFromNextServerContext = ( const createCookieStorageAdapterFromNextRequestAndNextResponse = ( request: NextRequest, - response: NextResponse + response: NextResponse, ): CookieStorage.Adapter => { const readonlyCookieStore = request.cookies; const mutableCookieStore = response.cookies; @@ -93,11 +93,11 @@ const createCookieStorageAdapterFromNextRequestAndNextResponse = ( const createCookieStorageAdapterFromNextRequestAndHttpResponse = ( request: NextRequest, - response: Response + response: Response, ): CookieStorage.Adapter => { const readonlyCookieStore = request.cookies; const mutableCookieStore = createMutableCookieStoreFromHeaders( - response.headers + response.headers, ); return { @@ -110,7 +110,7 @@ const createCookieStorageAdapterFromNextRequestAndHttpResponse = ( }; const createCookieStorageAdapterFromNextCookies = ( - cookies: NextServer.ServerComponentContext['cookies'] + cookies: NextServer.ServerComponentContext['cookies'], ): CookieStorage.Adapter => { const cookieStore = cookies(); @@ -147,7 +147,7 @@ const createCookieStorageAdapterFromNextCookies = ( const createCookieStorageAdapterFromGetServerSidePropsContext = ( request: NextServer.GetServerSidePropsContext['request'], - response: NextServer.GetServerSidePropsContext['response'] + response: NextServer.GetServerSidePropsContext['response'], ): CookieStorage.Adapter => { const cookiesMap = { ...request.cookies }; const allCookies = Object.entries(cookiesMap).map(([name, value]) => ({ @@ -158,6 +158,7 @@ const createCookieStorageAdapterFromGetServerSidePropsContext = ( return { get(name) { const value = cookiesMap[ensureEncodedForJSCookie(name)]; + return value ? { name, @@ -173,39 +174,40 @@ const createCookieStorageAdapterFromGetServerSidePropsContext = ( 'Set-Cookie', `${ensureEncodedForJSCookie(name)}=${value};${ options ? serializeSetCookieOptions(options) : '' - }` + }`, ); }, delete(name) { response.setHeader( 'Set-Cookie', `${ensureEncodedForJSCookie( - name - )}=;Expires=${DATE_IN_THE_PAST.toUTCString()}` + name, + )}=;Expires=${DATE_IN_THE_PAST.toUTCString()}`, ); }, }; }; const createMutableCookieStoreFromHeaders = ( - headers: Headers + headers: Headers, ): Pick => { const setFunc: CookieStorage.Adapter['set'] = (name, value, options) => { headers.append( 'Set-Cookie', `${ensureEncodedForJSCookie(name)}=${value};${ options ? serializeSetCookieOptions(options) : '' - }` + }`, ); }; const deleteFunc: CookieStorage.Adapter['delete'] = name => { headers.append( 'Set-Cookie', `${ensureEncodedForJSCookie( - name - )}=;Expires=${DATE_IN_THE_PAST.toUTCString()}` + name, + )}=;Expires=${DATE_IN_THE_PAST.toUTCString()}`, ); }; + return { set: setFunc, delete: deleteFunc, @@ -213,7 +215,7 @@ const createMutableCookieStoreFromHeaders = ( }; const serializeSetCookieOptions = ( - options: CookieStorage.SetCookieOptions + options: CookieStorage.SetCookieOptions, ): string => { const { expires, domain, httpOnly, sameSite, secure } = options; const serializedOptions: string[] = []; @@ -232,6 +234,7 @@ const serializeSetCookieOptions = ( if (secure) { serializedOptions.push(`Secure`); } + return serializedOptions.join(';'); }; diff --git a/packages/adapter-nextjs/src/utils/createRunWithAmplifyServerContext.ts b/packages/adapter-nextjs/src/utils/createRunWithAmplifyServerContext.ts index 6362d1149a8..3fef90ede2b 100644 --- a/packages/adapter-nextjs/src/utils/createRunWithAmplifyServerContext.ts +++ b/packages/adapter-nextjs/src/utils/createRunWithAmplifyServerContext.ts @@ -8,9 +8,9 @@ import { createUserPoolsTokenProvider, runWithAmplifyServerContext as runWithAmplifyServerContextCore, } from 'aws-amplify/adapter-core'; +import { NextServer } from '~/src/types'; import { createCookieStorageAdapterFromNextServerContext } from './createCookieStorageAdapterFromNextServerContext'; -import { NextServer } from '../types'; export const createRunWithAmplifyServerContext = ({ config: resourcesConfig, @@ -31,16 +31,16 @@ export const createRunWithAmplifyServerContext = ({ ? sharedInMemoryStorage : createKeyValueStorageFromCookieStorageAdapter( createCookieStorageAdapterFromNextServerContext( - nextServerContext - ) + nextServerContext, + ), ); const credentialsProvider = createAWSCredentialsAndIdentityIdProvider( resourcesConfig.Auth, - keyValueStorage + keyValueStorage, ); const tokenProvider = createUserPoolsTokenProvider( resourcesConfig.Auth, - keyValueStorage + keyValueStorage, ); return runWithAmplifyServerContextCore( @@ -48,7 +48,7 @@ export const createRunWithAmplifyServerContext = ({ { Auth: { credentialsProvider, tokenProvider }, }, - operation + operation, ); } diff --git a/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts b/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts index f3f16dbb868..a8ab9f1d22b 100644 --- a/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts +++ b/packages/adapter-nextjs/src/utils/getAmplifyConfig.ts @@ -6,7 +6,7 @@ import { LegacyConfig } from 'aws-amplify/adapter-core'; import { parseAWSExports } from '@aws-amplify/core/internals/utils'; export const getAmplifyConfig = ( - config: ResourcesConfig | LegacyConfig + config: ResourcesConfig | LegacyConfig, ): ResourcesConfig => Object.keys(config).some(key => key.startsWith('aws_')) ? parseAWSExports(config) diff --git a/packages/adapter-nextjs/tsconfig.build.json b/packages/adapter-nextjs/tsconfig.build.json index aed335f0bbc..93dc82e4dc9 100644 --- a/packages/adapter-nextjs/tsconfig.build.json +++ b/packages/adapter-nextjs/tsconfig.build.json @@ -1,8 +1,4 @@ { - "extends": "../tsconfig.base.json", - "compilerOptions": { - "strict": true, - "noImplicitAny": true - }, - "include": ["./src"] + "extends": "./tsconfig.json", + "exclude": ["__tests__"] } diff --git a/packages/adapter-nextjs/tsconfig.json b/packages/adapter-nextjs/tsconfig.json index 4cf76894da1..b5e4582c08b 100755 --- a/packages/adapter-nextjs/tsconfig.json +++ b/packages/adapter-nextjs/tsconfig.json @@ -1,21 +1,13 @@ -//WARNING: If you are manually specifying files to compile then the tsconfig.json is completely ignored, you must use command line flags { + "extends": "../../tsconfig.json", "compilerOptions": { "allowSyntheticDefaultImports": true, - "target": "es2020", - "noImplicitAny": true, - "lib": ["dom", "es2019", "esnext.asynciterable"], - "module": "commonjs", - "moduleResolution": "node", - "allowJs": true, - "declaration": true, - "typeRoots": ["./node_modules/@types", "../../node_modules/@types"], - "types": ["node"], - "esModuleInterop": true, - "resolveJsonModule": true, - "strict": true, - "alwaysStrict": true + "alwaysStrict": true, + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["src/**/*"], - "exclude": ["src/setupTests.ts"] + "include": ["./src", "__tests__"] } diff --git a/packages/adapter-nextjs/tsconfig.test.json b/packages/adapter-nextjs/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/adapter-nextjs/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/adapter-nextjs/tsconfig.watch.json b/packages/adapter-nextjs/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/adapter-nextjs/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/adapter-nextjs/tslint.json b/packages/adapter-nextjs/tslint.json deleted file mode 100644 index fcca611fccf..00000000000 --- a/packages/adapter-nextjs/tslint.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [ - true, - { - "limit": 120, - "ignore-pattern": "^//|^ *" - } - ], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/analytics/.eslintrc.js b/packages/analytics/.eslintrc.js new file mode 100644 index 00000000000..e841e30b64c --- /dev/null +++ b/packages/analytics/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/analytics', + }, + ], + }, +}; diff --git a/packages/analytics/__tests__/providers/personalize/utils/cachedSession.test.ts b/packages/analytics/__tests__/providers/personalize/utils/cachedSession.test.ts index 10681b33c6e..21c66b399d7 100644 --- a/packages/analytics/__tests__/providers/personalize/utils/cachedSession.test.ts +++ b/packages/analytics/__tests__/providers/personalize/utils/cachedSession.test.ts @@ -25,13 +25,15 @@ describe('Analytics service provider Personalize utils: cachedSession', () => { userId: 'userId0', }; - const mockCachedStorage = { + const mockCachedStorage: Record = { [userIdCacheKey]: mockSession.userId, [sessionIdCacheKey]: mockSession.sessionId, }; beforeEach(() => { - mockCache.getItem.mockImplementation(key => mockCachedStorage[key]); + mockCache.getItem.mockImplementation(key => + Promise.resolve(mockCachedStorage[key]), + ); mockIsBrowser.mockReturnValue(false); mockAmplifyUuid.mockReturnValue(mockUuid); }); @@ -62,13 +64,13 @@ describe('Analytics service provider Personalize utils: cachedSession', () => { 1, sessionIdCacheKey, expect.any(String), - expect.any(Object) + expect.any(Object), ); expect(mockCache.setItem).toHaveBeenNthCalledWith( 2, userIdCacheKey, 'newUserId', - expect.any(Object) + expect.any(Object), ); }); @@ -79,13 +81,13 @@ describe('Analytics service provider Personalize utils: cachedSession', () => { 1, sessionIdCacheKey, expect.any(String), - expect.any(Object) + expect.any(Object), ); expect(mockCache.setItem).toHaveBeenNthCalledWith( 2, userIdCacheKey, undefined, - expect.any(Object) + expect.any(Object), ); }); @@ -96,13 +98,13 @@ describe('Analytics service provider Personalize utils: cachedSession', () => { 1, sessionIdCacheKey, expect.any(String), - expect.any(Object) + expect.any(Object), ); expect(mockCache.setItem).toHaveBeenNthCalledWith( 2, userIdCacheKey, 'newUserId', - expect.any(Object) + expect.any(Object), ); }); @@ -113,7 +115,7 @@ describe('Analytics service provider Personalize utils: cachedSession', () => { 1, userIdCacheKey, 'newUserId', - expect.any(Object) + expect.any(Object), ); }); }); diff --git a/packages/analytics/jest.config.js b/packages/analytics/jest.config.js deleted file mode 100644 index 96f85fb0774..00000000000 --- a/packages/analytics/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 42, - functions: 58, - lines: 65, - statements: 68, - }, - }, - moduleNameMapper: { - uuid: require.resolve('uuid'), - }, -}; diff --git a/packages/analytics/jest.config.mjs b/packages/analytics/jest.config.mjs new file mode 100644 index 00000000000..70544aaad64 --- /dev/null +++ b/packages/analytics/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 42, + functions: 58, + lines: 65, + statements: 68, + }, + }, + moduleNameMapper: { + uuid: requireResolve('uuid'), + }, +}); + +export default jestConfig; diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 792f624f1d7..9aceaa70c89 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -11,19 +11,20 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:watch": "tslint 'src/**/*.ts' && jest -w 1 --watch", "build-with-test": "npm run clean && npm run test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 71.4" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 71.4" }, "typesVersions": { ">=4.2": { @@ -108,7 +109,6 @@ "@aws-amplify/react-native": "1.0.8", "@aws-sdk/types": "3.398.0", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" } } diff --git a/packages/analytics/src/apis/disable.ts b/packages/analytics/src/apis/disable.ts index 4a23cfce970..75a4cf0b390 100644 --- a/packages/analytics/src/apis/disable.ts +++ b/packages/analytics/src/apis/disable.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { disableAnalytics } from '../utils'; +import { disableAnalytics } from '~/src/utils'; /** * Disables the Analytics category. diff --git a/packages/analytics/src/apis/enable.ts b/packages/analytics/src/apis/enable.ts index a2f4c72e6f1..ef4058f3972 100644 --- a/packages/analytics/src/apis/enable.ts +++ b/packages/analytics/src/apis/enable.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { enableAnalytics } from '../utils'; +import { enableAnalytics } from '~/src/utils'; /** * Enables the Analytics category to permit the transmission of events. diff --git a/packages/analytics/src/errors/assertValidationError.ts b/packages/analytics/src/errors/assertValidationError.ts index 6eddaffd9ba..17af548d54e 100644 --- a/packages/analytics/src/errors/assertValidationError.ts +++ b/packages/analytics/src/errors/assertValidationError.ts @@ -10,7 +10,7 @@ import { AnalyticsValidationErrorCode, validationErrorMap } from './validation'; export function assertValidationError( assertion: boolean, name: AnalyticsValidationErrorCode, - message?: string + message?: string, ): asserts assertion { const { message: defaultMessage, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/analytics/src/providers/kinesis-firehose/apis/flushEvents.ts b/packages/analytics/src/providers/kinesis-firehose/apis/flushEvents.ts index 981a7a8cc93..ff845708987 100644 --- a/packages/analytics/src/providers/kinesis-firehose/apis/flushEvents.ts +++ b/packages/analytics/src/providers/kinesis-firehose/apis/flushEvents.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getEventBuffer, resolveConfig } from '../utils'; -import { - getAnalyticsUserAgentString, - resolveCredentials, -} from '../../../utils'; import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { + getEventBuffer, + resolveConfig, +} from '~/src/providers/kinesis-firehose/utils'; +import { getAnalyticsUserAgentString, resolveCredentials } from '~/src/utils'; const logger = new ConsoleLogger('KinesisFirehose'); @@ -32,8 +32,10 @@ export const flushEvents = () => { identityId, resendLimit, userAgentValue: getAnalyticsUserAgentString(AnalyticsAction.Record), - }) + }), ) .then(eventBuffer => eventBuffer.flushAll()) - .catch(e => logger.warn('Failed to flush events.', e)); + .catch(e => { + logger.warn('Failed to flush events.', e); + }); }; diff --git a/packages/analytics/src/providers/kinesis-firehose/apis/record.ts b/packages/analytics/src/providers/kinesis-firehose/apis/record.ts index 917dd95f66f..63c6c04b950 100644 --- a/packages/analytics/src/providers/kinesis-firehose/apis/record.ts +++ b/packages/analytics/src/providers/kinesis-firehose/apis/record.ts @@ -1,22 +1,26 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { RecordInput } from '../types'; -import { getEventBuffer, resolveConfig } from '../utils'; +import { fromUtf8 } from '@smithy/util-utf8'; +import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; +import { ConsoleLogger } from '@aws-amplify/core'; +import { RecordInput } from '~/src/providers/kinesis-firehose/types'; +import { + getEventBuffer, + resolveConfig, +} from '~/src/providers/kinesis-firehose/utils'; import { getAnalyticsUserAgentString, isAnalyticsEnabled, resolveCredentials, -} from '../../../utils'; -import { fromUtf8 } from '@smithy/util-utf8'; -import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; -import { ConsoleLogger } from '@aws-amplify/core'; +} from '~/src/utils'; const logger = new ConsoleLogger('KinesisFirehose'); export const record = ({ streamName, data }: RecordInput): void => { if (!isAnalyticsEnabled()) { logger.debug('Analytics is disabled, event will not be recorded.'); + return; } diff --git a/packages/analytics/src/providers/kinesis-firehose/types/buffer.ts b/packages/analytics/src/providers/kinesis-firehose/types/buffer.ts index 6ce03a616e1..c6ee28144e8 100644 --- a/packages/analytics/src/providers/kinesis-firehose/types/buffer.ts +++ b/packages/analytics/src/providers/kinesis-firehose/types/buffer.ts @@ -1,9 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventBufferConfig } from '../../../utils'; -import { KinesisStream } from '../../../types'; import { AWSCredentials } from '@aws-amplify/core/internals/utils'; +import { EventBufferConfig } from '~/src/utils'; +import { KinesisStream } from '~/src/types'; export type KinesisFirehoseBufferEvent = KinesisStream & { event: Uint8Array; diff --git a/packages/analytics/src/providers/kinesis-firehose/types/inputs.ts b/packages/analytics/src/providers/kinesis-firehose/types/inputs.ts index 12a84b1ff56..52f373e3b22 100644 --- a/packages/analytics/src/providers/kinesis-firehose/types/inputs.ts +++ b/packages/analytics/src/providers/kinesis-firehose/types/inputs.ts @@ -1,9 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KinesisEventData } from '../../../types'; +import { KinesisEventData } from '~/src/types'; -export type RecordInput = { +export interface RecordInput { streamName: string; data: KinesisEventData; -}; +} diff --git a/packages/analytics/src/providers/kinesis-firehose/utils/getEventBuffer.ts b/packages/analytics/src/providers/kinesis-firehose/utils/getEventBuffer.ts index 843459d1428..df188cf2ddd 100644 --- a/packages/analytics/src/providers/kinesis-firehose/utils/getEventBuffer.ts +++ b/packages/analytics/src/providers/kinesis-firehose/utils/getEventBuffer.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventBuffer, groupBy, IAnalyticsClient } from '../../../utils'; import { FirehoseClient, PutRecordBatchCommand, } from '@aws-sdk/client-firehose'; +import { EventBuffer, IAnalyticsClient, groupBy } from '~/src/utils'; import { KinesisFirehoseBufferEvent, KinesisFirehoseEventBufferConfig, -} from '../types'; +} from '~/src/providers/kinesis-firehose/types'; /** * These Records hold cached event buffers and AWS clients. @@ -27,7 +27,7 @@ const cachedClients: Record = {}; const createPutRecordsBatchCommand = ( streamName: string, - events: KinesisFirehoseBufferEvent[] + events: KinesisFirehoseBufferEvent[], ): PutRecordBatchCommand => new PutRecordBatchCommand({ DeliveryStreamName: streamName, @@ -39,24 +39,25 @@ const createPutRecordsBatchCommand = ( const submitEvents = async ( events: KinesisFirehoseBufferEvent[], client: FirehoseClient, - resendLimit?: number + resendLimit?: number, ): Promise => { const groupedByStreamName = Object.entries( - groupBy(event => event.streamName, events) + groupBy(event => event.streamName, events), ); const requests = groupedByStreamName - .map(([streamName, events]) => - createPutRecordsBatchCommand(streamName, events) + .map(([streamName, streamEvents]) => + createPutRecordsBatchCommand(streamName, streamEvents), ) .map(command => client.send(command)); const responses = await Promise.allSettled(requests); const failedEvents = responses .map((response, i) => - response.status === 'rejected' ? groupedByStreamName[i][1] : [] + response.status === 'rejected' ? groupedByStreamName[i][1] : [], ) .flat(); + return resendLimit ? failedEvents .filter(event => event.retryCount < resendLimit) @@ -88,6 +89,7 @@ export const getEventBuffer = ({ } const firehoseClient = cachedClients[sessionIdentityKey]; + return events => submitEvents(events, firehoseClient, resendLimit); }; @@ -98,11 +100,11 @@ export const getEventBuffer = ({ flushSize, flushInterval, }, - getClient + getClient, ); const releaseSessionKeys = Object.keys(eventBufferMap).filter( - key => key !== sessionIdentityKey + key => key !== sessionIdentityKey, ); for (const releaseSessionKey of releaseSessionKeys) { eventBufferMap[releaseSessionKey].flushAll().finally(() => { diff --git a/packages/analytics/src/providers/kinesis-firehose/utils/resolveConfig.ts b/packages/analytics/src/providers/kinesis-firehose/utils/resolveConfig.ts index 7ce9969a259..542e3b0da8a 100644 --- a/packages/analytics/src/providers/kinesis-firehose/utils/resolveConfig.ts +++ b/packages/analytics/src/providers/kinesis-firehose/utils/resolveConfig.ts @@ -5,7 +5,8 @@ import { Amplify } from '@aws-amplify/core'; import { AnalyticsValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/errors'; + import { DEFAULT_KINESIS_FIREHOSE_CONFIG } from './constants'; export const resolveConfig = () => { @@ -24,8 +25,9 @@ export const resolveConfig = () => { assertValidationError(!!region, AnalyticsValidationErrorCode.NoRegion); assertValidationError( flushSize < bufferSize, - AnalyticsValidationErrorCode.InvalidFlushSize + AnalyticsValidationErrorCode.InvalidFlushSize, ); + return { region, bufferSize, diff --git a/packages/analytics/src/providers/kinesis/apis/flushEvents.ts b/packages/analytics/src/providers/kinesis/apis/flushEvents.ts index 2937aa355a2..de7225c03c5 100644 --- a/packages/analytics/src/providers/kinesis/apis/flushEvents.ts +++ b/packages/analytics/src/providers/kinesis/apis/flushEvents.ts @@ -1,14 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { resolveConfig } from '../utils/resolveConfig'; -import { - getAnalyticsUserAgentString, - resolveCredentials, -} from '../../../utils'; -import { getEventBuffer } from '../utils/getEventBuffer'; import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { resolveConfig } from '~/src/providers/kinesis/utils/resolveConfig'; +import { getAnalyticsUserAgentString, resolveCredentials } from '~/src/utils'; +import { getEventBuffer } from '~/src/providers/kinesis/utils/getEventBuffer'; const logger = new ConsoleLogger('Kinesis'); @@ -33,8 +30,10 @@ export const flushEvents = () => { identityId, resendLimit, userAgentValue: getAnalyticsUserAgentString(AnalyticsAction.Record), - }) + }), ) .then(eventBuffer => eventBuffer.flushAll()) - .catch(e => logger.warn('Failed to flush events.', e)); + .catch(e => { + logger.warn('Failed to flush events.', e); + }); }; diff --git a/packages/analytics/src/providers/kinesis/apis/record.ts b/packages/analytics/src/providers/kinesis/apis/record.ts index dbfabab423e..6bcba759b48 100644 --- a/packages/analytics/src/providers/kinesis/apis/record.ts +++ b/packages/analytics/src/providers/kinesis/apis/record.ts @@ -1,17 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { RecordInput } from '../types'; -import { getEventBuffer } from '../utils/getEventBuffer'; -import { resolveConfig } from '../utils/resolveConfig'; +import { fromUtf8 } from '@smithy/util-utf8'; +import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; +import { ConsoleLogger } from '@aws-amplify/core'; +import { RecordInput } from '~/src/providers/kinesis/types'; +import { getEventBuffer } from '~/src/providers/kinesis/utils/getEventBuffer'; +import { resolveConfig } from '~/src/providers/kinesis/utils/resolveConfig'; import { getAnalyticsUserAgentString, isAnalyticsEnabled, resolveCredentials, -} from '../../../utils'; -import { fromUtf8 } from '@smithy/util-utf8'; -import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; -import { ConsoleLogger } from '@aws-amplify/core'; +} from '~/src/utils'; const logger = new ConsoleLogger('Kinesis'); @@ -22,6 +22,7 @@ export const record = ({ }: RecordInput): void => { if (!isAnalyticsEnabled()) { logger.debug('Analytics is disabled, event will not be recorded.'); + return; } diff --git a/packages/analytics/src/providers/kinesis/types/buffer.ts b/packages/analytics/src/providers/kinesis/types/buffer.ts index 57ab85ecb34..0539c4a6bbb 100644 --- a/packages/analytics/src/providers/kinesis/types/buffer.ts +++ b/packages/analytics/src/providers/kinesis/types/buffer.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { AWSCredentials } from '@aws-amplify/core/internals/utils'; -import { EventBufferConfig } from '../../../utils'; -import { KinesisShard } from '../../../types'; +import { EventBufferConfig } from '~/src/utils'; +import { KinesisShard } from '~/src/types'; export type KinesisBufferEvent = KinesisShard & { event: Uint8Array; diff --git a/packages/analytics/src/providers/kinesis/types/inputs.ts b/packages/analytics/src/providers/kinesis/types/inputs.ts index 5d59d527447..704219c99c3 100644 --- a/packages/analytics/src/providers/kinesis/types/inputs.ts +++ b/packages/analytics/src/providers/kinesis/types/inputs.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KinesisEventData } from '../../../types'; +import { KinesisEventData } from '~/src/types'; -export type RecordInput = { +export interface RecordInput { streamName: string; partitionKey: string; data: KinesisEventData; -}; +} diff --git a/packages/analytics/src/providers/kinesis/utils/getEventBuffer.ts b/packages/analytics/src/providers/kinesis/utils/getEventBuffer.ts index 51dbfef8d82..307055a2767 100644 --- a/packages/analytics/src/providers/kinesis/utils/getEventBuffer.ts +++ b/packages/analytics/src/providers/kinesis/utils/getEventBuffer.ts @@ -1,9 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KinesisBufferEvent, KinesisEventBufferConfig } from '../types'; -import { EventBuffer, groupBy, IAnalyticsClient } from '../../../utils'; import { KinesisClient, PutRecordsCommand } from '@aws-sdk/client-kinesis'; +import { + KinesisBufferEvent, + KinesisEventBufferConfig, +} from '~/src/providers/kinesis/types'; +import { EventBuffer, IAnalyticsClient, groupBy } from '~/src/utils'; /** * These Records hold cached event buffers and AWS clients. @@ -18,7 +21,7 @@ const cachedClients: Record = {}; const createKinesisPutRecordsCommand = ( streamName: string, - events: KinesisBufferEvent[] + events: KinesisBufferEvent[], ): PutRecordsCommand => new PutRecordsCommand({ StreamName: streamName, @@ -31,23 +34,24 @@ const createKinesisPutRecordsCommand = ( const submitEvents = async ( events: KinesisBufferEvent[], client: KinesisClient, - resendLimit?: number + resendLimit?: number, ): Promise => { const groupedByStreamName = Object.entries( - groupBy(event => event.streamName, events) + groupBy(event => event.streamName, events), ); const requests = groupedByStreamName - .map(([streamName, events]) => - createKinesisPutRecordsCommand(streamName, events) + .map(([streamName, streamEvents]) => + createKinesisPutRecordsCommand(streamName, streamEvents), ) .map(command => client.send(command)); const responses = await Promise.allSettled(requests); const failedEvents = responses .map((response, i) => - response.status === 'rejected' ? groupedByStreamName[i][1] : [] + response.status === 'rejected' ? groupedByStreamName[i][1] : [], ) .flat(); + return resendLimit ? failedEvents .filter(event => event.retryCount < resendLimit) @@ -89,12 +93,12 @@ export const getEventBuffer = ({ flushSize, bufferSize, }, - getKinesisClient + getKinesisClient, ); // release other sessions const releaseSessionKeys = Object.keys(eventBufferMap).filter( - x => x !== sessionIdentityKey + x => x !== sessionIdentityKey, ); for (const releaseSessionKey of releaseSessionKeys) { eventBufferMap[releaseSessionKey].flushAll().finally(() => { diff --git a/packages/analytics/src/providers/kinesis/utils/resolveConfig.ts b/packages/analytics/src/providers/kinesis/utils/resolveConfig.ts index 2eaf8edf4ff..d7132525ffd 100644 --- a/packages/analytics/src/providers/kinesis/utils/resolveConfig.ts +++ b/packages/analytics/src/providers/kinesis/utils/resolveConfig.ts @@ -5,7 +5,8 @@ import { Amplify } from '@aws-amplify/core'; import { AnalyticsValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/errors'; + import { DEFAULT_KINESIS_CONFIG } from './constants'; export const resolveConfig = () => { @@ -24,8 +25,9 @@ export const resolveConfig = () => { assertValidationError(!!region, AnalyticsValidationErrorCode.NoRegion); assertValidationError( flushSize < bufferSize, - AnalyticsValidationErrorCode.InvalidFlushSize + AnalyticsValidationErrorCode.InvalidFlushSize, ); + return { region, bufferSize, diff --git a/packages/analytics/src/providers/personalize/apis/flushEvents.ts b/packages/analytics/src/providers/personalize/apis/flushEvents.ts index 0b4b489df27..50d14505e4c 100644 --- a/packages/analytics/src/providers/personalize/apis/flushEvents.ts +++ b/packages/analytics/src/providers/personalize/apis/flushEvents.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getEventBuffer, resolveConfig } from '../utils'; -import { - getAnalyticsUserAgentString, - resolveCredentials, -} from '../../../utils'; import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { + getEventBuffer, + resolveConfig, +} from '~/src/providers/personalize/utils'; +import { getAnalyticsUserAgentString, resolveCredentials } from '~/src/utils'; const logger = new ConsoleLogger('Personalize'); @@ -30,8 +30,10 @@ export const flushEvents = () => { credentials, identityId, userAgentValue: getAnalyticsUserAgentString(AnalyticsAction.Record), - }) + }), ) .then(eventBuffer => eventBuffer.flushAll()) - .catch(e => logger.warn('Failed to flush events', e)); + .catch(e => { + logger.warn('Failed to flush events', e); + }); }; diff --git a/packages/analytics/src/providers/personalize/apis/record.ts b/packages/analytics/src/providers/personalize/apis/record.ts index 1ee479909a4..679d0804bd9 100644 --- a/packages/analytics/src/providers/personalize/apis/record.ts +++ b/packages/analytics/src/providers/personalize/apis/record.ts @@ -1,25 +1,25 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { RecordInput } from '../types'; +import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; +import { ConsoleLogger } from '@aws-amplify/core'; +import { RecordInput } from '~/src/providers/personalize/types'; import { autoTrackMedia, getEventBuffer, resolveCachedSession, resolveConfig, updateCachedSession, -} from '../utils'; +} from '~/src/providers/personalize/utils'; import { getAnalyticsUserAgentString, isAnalyticsEnabled, resolveCredentials, -} from '../../../utils'; -import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; -import { ConsoleLogger } from '@aws-amplify/core'; +} from '~/src/utils'; import { IDENTIFY_EVENT_TYPE, MEDIA_AUTO_TRACK_EVENT_TYPE, -} from '../utils/constants'; +} from '~/src/providers/personalize/utils/constants'; const logger = new ConsoleLogger('Personalize'); @@ -31,6 +31,7 @@ export const record = ({ }: RecordInput): void => { if (!isAnalyticsEnabled()) { logger.debug('Analytics is disabled, event will not be recorded.'); + return; } @@ -45,9 +46,9 @@ export const record = ({ updateCachedSession( typeof properties.userId === 'string' ? properties.userId : '', cachedSessionId, - cachedUserId + cachedUserId, ); - } else if (!!userId) { + } else if (userId) { updateCachedSession(userId, cachedSessionId, cachedUserId); } @@ -76,7 +77,7 @@ export const record = ({ properties, }, }, - eventBuffer + eventBuffer, ); } else { eventBuffer.append({ diff --git a/packages/analytics/src/providers/personalize/types/buffer.ts b/packages/analytics/src/providers/personalize/types/buffer.ts index 85881d6981c..1ae42f558ef 100644 --- a/packages/analytics/src/providers/personalize/types/buffer.ts +++ b/packages/analytics/src/providers/personalize/types/buffer.ts @@ -1,17 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PersonalizeEvent } from './'; -import { EventBufferConfig } from '../../../utils'; import { AWSCredentials } from '@aws-amplify/core/internals/utils'; +import { EventBufferConfig } from '~/src/utils'; -export type PersonalizeBufferEvent = { +import { PersonalizeEvent } from './inputs'; + +export interface PersonalizeBufferEvent { trackingId: string; sessionId?: string; userId?: string; event: PersonalizeEvent; timestamp: number; -}; +} export type PersonalizeBufferConfig = EventBufferConfig & { region: string; diff --git a/packages/analytics/src/providers/personalize/types/inputs.ts b/packages/analytics/src/providers/personalize/types/inputs.ts index 80e440c2873..fe05c98758d 100644 --- a/packages/analytics/src/providers/personalize/types/inputs.ts +++ b/packages/analytics/src/providers/personalize/types/inputs.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type PersonalizeEvent = { +export interface PersonalizeEvent { userId?: string; eventId?: string; eventType: string; properties: Record; -}; +} export type RecordInput = PersonalizeEvent; diff --git a/packages/analytics/src/providers/personalize/utils/autoTrackMedia.ts b/packages/analytics/src/providers/personalize/utils/autoTrackMedia.ts index cda7149af24..cbd3bf46cde 100644 --- a/packages/analytics/src/providers/personalize/utils/autoTrackMedia.ts +++ b/packages/analytics/src/providers/personalize/utils/autoTrackMedia.ts @@ -1,10 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventBuffer } from '../../../utils'; -import { PersonalizeBufferEvent, PersonalizeEvent } from '../types'; import { isBrowser } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { EventBuffer } from '~/src/utils'; +import { + PersonalizeBufferEvent, + PersonalizeEvent, +} from '~/src/providers/personalize/types'; enum HTML5_MEDIA_EVENT { 'PLAY' = 'play', @@ -25,28 +28,30 @@ enum EVENT_TYPE { 'TIME_WATCHED' = 'TimeWatched', } -interface IRecordEvent { - (eventType: string, properties: Record): void; -} +type IRecordEvent = ( + eventType: string, + properties: Record, +) => void; -type MediaAutoTrackConfig = { +interface MediaAutoTrackConfig { trackingId: string; sessionId: string; userId?: string; event: PersonalizeEvent; -}; +} const logger = new ConsoleLogger('MediaAutoTrack'); const startIframeAutoTracking = ( element: HTMLElement, - recordEvent: IRecordEvent + recordEvent: IRecordEvent, ) => { let isPlaying = false; let player: any; const mediaProperties = (): Record => { const duration = Number(parseFloat(player.getDuration()).toFixed(4)); const currentTime = Number(parseFloat(player.getCurrentTime()).toFixed(4)); + return { duration, eventValue: Number((currentTime / duration).toFixed(4)), @@ -64,15 +69,16 @@ const startIframeAutoTracking = ( } }, 3_000); - element.addEventListener('unload', () => clearInterval(timer)); + element.addEventListener('unload', () => { + clearInterval(timer); + }); + + const windowGlobal = {} as any; - // @ts-ignore - window.onYouTubeIframeAPIReady = () => { - // @ts-ignore - delete window.onYouTubeIframeAPIReady; + windowGlobal.onYouTubeIframeAPIReady = () => { + delete windowGlobal.onYouTubeIframeAPIReady; - // @ts-ignore - player = new window.YT.Player(element.id, { + player = new windowGlobal.YT.Player(element.id, { events: { onStateChange: (event: any) => { const iframeEventMapping = { @@ -80,8 +86,9 @@ const startIframeAutoTracking = ( 1: EVENT_TYPE.PLAY, 2: EVENT_TYPE.PAUSE, }; - // @ts-ignore - const eventType = iframeEventMapping[event.data]; + + const eventType = + iframeEventMapping[event.data as keyof typeof iframeEventMapping]; switch (eventType) { case EVENT_TYPE.ENDED: case EVENT_TYPE.PAUSE: @@ -103,7 +110,7 @@ const startIframeAutoTracking = ( const startHTMLMediaAutoTracking = ( element: HTMLMediaElement, - recordEvent: IRecordEvent + recordEvent: IRecordEvent, ) => { let isPlaying = false; const mediaProperties = (): Record => ({ @@ -117,7 +124,9 @@ const startHTMLMediaAutoTracking = ( } }, 3_000); - element.addEventListener('unload', () => clearInterval(timer)); + element.addEventListener('unload', () => { + clearInterval(timer); + }); element.addEventListener(HTML5_MEDIA_EVENT.PLAY, () => { isPlaying = true; @@ -137,7 +146,7 @@ const startHTMLMediaAutoTracking = ( const checkElementLoaded = (interval: number, maxTries: number) => { let retryCount = 0; - const wait = () => new Promise(r => setTimeout(r, interval)); + const wait = () => new Promise(resolve => setTimeout(resolve, interval)); const check = async (elementId: string): Promise => { if (retryCount >= maxTries) { return false; @@ -149,18 +158,19 @@ const checkElementLoaded = (interval: number, maxTries: number) => { } else { retryCount += 1; await wait(); - return await check(elementId); + + return check(elementId); } }; + return check; }; -const recordEvent = - ( - config: MediaAutoTrackConfig, - eventBuffer: EventBuffer - ): IRecordEvent => - (eventType: string, properties: Record) => { +const recordEvent = ( + config: MediaAutoTrackConfig, + eventBuffer: EventBuffer, +): IRecordEvent => { + return (eventType: string, properties: Record) => { // override eventType and merge properties eventBuffer.append({ ...config, @@ -175,22 +185,25 @@ const recordEvent = timestamp: Date.now(), }); }; +}; export const autoTrackMedia = async ( config: MediaAutoTrackConfig, - eventBuffer: EventBuffer + eventBuffer: EventBuffer, ) => { const { eventType, properties } = config.event; const { domElementId, ...otherProperties } = properties; if (!isBrowser()) { logger.debug(`${eventType} only for browser`); + return; } if (typeof domElementId === 'string' && !domElementId) { logger.debug( - "Missing domElementId field in 'properties' for MediaAutoTrack event type." + "Missing domElementId field in 'properties' for MediaAutoTrack event type.", ); + return; } @@ -210,7 +223,7 @@ export const autoTrackMedia = async ( case MEDIA_TYPE.IFRAME: startIframeAutoTracking( element, - recordEvent(autoTrackConfigWithoutDomElementId, eventBuffer) + recordEvent(autoTrackConfigWithoutDomElementId, eventBuffer), ); break; case MEDIA_TYPE.VIDEO: @@ -218,7 +231,7 @@ export const autoTrackMedia = async ( if (element instanceof HTMLMediaElement) { startHTMLMediaAutoTracking( element, - recordEvent(autoTrackConfigWithoutDomElementId, eventBuffer) + recordEvent(autoTrackConfigWithoutDomElementId, eventBuffer), ); } break; diff --git a/packages/analytics/src/providers/personalize/utils/cachedSession.ts b/packages/analytics/src/providers/personalize/utils/cachedSession.ts index e791fc5691f..59e3c85d260 100644 --- a/packages/analytics/src/providers/personalize/utils/cachedSession.ts +++ b/packages/analytics/src/providers/personalize/utils/cachedSession.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Cache } from '@aws-amplify/core'; -import { isBrowser, amplifyUuid } from '@aws-amplify/core/internals/utils'; +import { amplifyUuid, isBrowser } from '@aws-amplify/core/internals/utils'; const PERSONALIZE_CACHE_USERID = '_awsct_uid'; const PERSONALIZE_CACHE_SESSIONID = '_awsct_sid'; @@ -12,14 +12,14 @@ const CACHE_EXPIRY_IN_DAYS = 7; const normalize = (key: string): string => [key, isBrowser() ? window.location.host : DEFAULT_CACHE_PREFIX].join( - DELIMITER + DELIMITER, ); const getCache = (key: string) => Cache.getItem(normalize(key)); const setCache = (key: string, value: unknown) => { const expiredAt = new Date( - Date.now() + 3_600_000 * 24 * CACHE_EXPIRY_IN_DAYS + Date.now() + 3_600_000 * 24 * CACHE_EXPIRY_IN_DAYS, ); Cache.setItem(normalize(key), value, { expires: expiredAt.getTime(), @@ -28,7 +28,7 @@ const setCache = (key: string, value: unknown) => { export const resolveCachedSession = async () => { let sessionId: string | undefined = await getCache( - PERSONALIZE_CACHE_SESSIONID + PERSONALIZE_CACHE_SESSIONID, ); if (!sessionId) { sessionId = amplifyUuid(); @@ -46,7 +46,7 @@ export const resolveCachedSession = async () => { export const updateCachedSession = ( newUserId?: string, currentSessionId?: string, - currentUserId?: string + currentUserId?: string, ) => { const isNoCachedSession = !currentSessionId; const isSignOutCase = !newUserId && !currentUserId; diff --git a/packages/analytics/src/providers/personalize/utils/getEventBuffer.ts b/packages/analytics/src/providers/personalize/utils/getEventBuffer.ts index a0e64f9a969..1047f52039e 100644 --- a/packages/analytics/src/providers/personalize/utils/getEventBuffer.ts +++ b/packages/analytics/src/providers/personalize/utils/getEventBuffer.ts @@ -1,12 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventBuffer, groupBy, IAnalyticsClient } from '../../../utils'; -import { PersonalizeBufferConfig, PersonalizeBufferEvent } from '../types'; import { PersonalizeEventsClient, PutEventsCommand, } from '@aws-sdk/client-personalize-events'; +import { EventBuffer, IAnalyticsClient, groupBy } from '~/src/utils'; +import { + PersonalizeBufferConfig, + PersonalizeBufferEvent, +} from '~/src/providers/personalize/types'; /** * These Records hold cached event buffers and AWS clients. @@ -23,9 +26,10 @@ const DELIMITER = '#'; const createPutEventsCommand = ( ids: string, - events: PersonalizeBufferEvent[] + events: PersonalizeBufferEvent[], ): PutEventsCommand => { const [trackingId, sessionId, userId] = ids.split(DELIMITER); + return new PutEventsCommand({ trackingId, sessionId, @@ -41,7 +45,7 @@ const createPutEventsCommand = ( const submitEvents = async ( events: PersonalizeBufferEvent[], - client: PersonalizeEventsClient + client: PersonalizeEventsClient, ): Promise => { const groupedByIds = Object.entries( groupBy( @@ -49,15 +53,16 @@ const submitEvents = async ( [event.trackingId, event.sessionId, event.userId] .filter(id => !!id) .join(DELIMITER), - events - ) + events, + ), ); const requests = groupedByIds - .map(([ids, events]) => createPutEventsCommand(ids, events)) + .map(([ids, groupedEvents]) => createPutEventsCommand(ids, groupedEvents)) .map(command => client.send(command)); await Promise.allSettled(requests); + return Promise.resolve([]); }; @@ -81,6 +86,7 @@ export const getEventBuffer = ({ customUserAgent: userAgentValue, }); } + return events => submitEvents(events, cachedClients[sessionIdentityKey]); }; @@ -91,11 +97,11 @@ export const getEventBuffer = ({ flushSize, flushInterval, }, - getClient + getClient, ); const releaseSessionKeys = Object.keys(eventBufferMap).filter( - key => key !== sessionIdentityKey + key => key !== sessionIdentityKey, ); for (const releaseSessionKey of releaseSessionKeys) { eventBufferMap[releaseSessionKey].flushAll().finally(() => { diff --git a/packages/analytics/src/providers/personalize/utils/resolveConfig.ts b/packages/analytics/src/providers/personalize/utils/resolveConfig.ts index 990a215b7ca..1c7ea185890 100644 --- a/packages/analytics/src/providers/personalize/utils/resolveConfig.ts +++ b/packages/analytics/src/providers/personalize/utils/resolveConfig.ts @@ -5,7 +5,8 @@ import { Amplify } from '@aws-amplify/core'; import { AnalyticsValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/errors'; + import { DEFAULT_PERSONALIZE_CONFIG, PERSONALIZE_FLUSH_SIZE_MAX, @@ -26,12 +27,12 @@ export const resolveConfig = () => { assertValidationError(!!region, AnalyticsValidationErrorCode.NoRegion); assertValidationError( !!trackingId, - AnalyticsValidationErrorCode.NoTrackingId + AnalyticsValidationErrorCode.NoTrackingId, ); assertValidationError( flushSize <= PERSONALIZE_FLUSH_SIZE_MAX, AnalyticsValidationErrorCode.InvalidFlushSize, - `FlushSize for Personalize should be less or equal than ${PERSONALIZE_FLUSH_SIZE_MAX}` + `FlushSize for Personalize should be less or equal than ${PERSONALIZE_FLUSH_SIZE_MAX}`, ); return { diff --git a/packages/analytics/src/providers/pinpoint/apis/configureAutoTrack.ts b/packages/analytics/src/providers/pinpoint/apis/configureAutoTrack.ts index e50fe6002bd..58d20eda574 100644 --- a/packages/analytics/src/providers/pinpoint/apis/configureAutoTrack.ts +++ b/packages/analytics/src/providers/pinpoint/apis/configureAutoTrack.ts @@ -1,17 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AnalyticsValidationErrorCode } from '../../../errors'; +import { UpdateEndpointException } from '@aws-amplify/core/internals/providers/pinpoint'; +import { AnalyticsValidationErrorCode } from '~/src/errors'; import { - TrackerType, TrackerAttributes, TrackerInterface, -} from '../../../types/trackers'; + TrackerType, +} from '~/src/types/trackers'; import { updateProviderTrackers, validateTrackerConfiguration, -} from '../../../utils'; -import { ConfigureAutoTrackInput } from '../types'; +} from '~/src/utils'; +import { ConfigureAutoTrackInput } from '~/src/providers/pinpoint/types'; + import { record } from './record'; // Configured Tracker instances for Pinpoint @@ -20,7 +22,7 @@ const configuredTrackers: Partial> = {}; // Callback that will emit an appropriate event to Pinpoint when required by the Tracker const emitTrackingEvent = ( eventName: string, - attributes: TrackerAttributes + attributes: TrackerAttributes, ) => { record({ name: eventName, diff --git a/packages/analytics/src/providers/pinpoint/apis/flushEvents.ts b/packages/analytics/src/providers/pinpoint/apis/flushEvents.ts index 4dc6425b77b..66822de5131 100644 --- a/packages/analytics/src/providers/pinpoint/apis/flushEvents.ts +++ b/packages/analytics/src/providers/pinpoint/apis/flushEvents.ts @@ -1,11 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { resolveConfig, resolveCredentials } from '../utils'; import { flushEvents as flushEventsCore } from '@aws-amplify/core/internals/providers/pinpoint'; import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; -import { getAnalyticsUserAgentString } from '../../../utils'; +import { + resolveConfig, + resolveCredentials, +} from '~/src/providers/pinpoint/utils'; +import { getAnalyticsUserAgentString } from '~/src/utils'; const logger = new ConsoleLogger('Analytics'); @@ -20,7 +23,7 @@ export const flushEvents = () => { const { appId, region, bufferSize, flushSize, flushInterval, resendLimit } = resolveConfig(); resolveCredentials() - .then(({ credentials, identityId }) => + .then(({ credentials, identityId }) => { flushEventsCore({ appId, region, @@ -31,7 +34,9 @@ export const flushEvents = () => { flushInterval, resendLimit, userAgentValue: getAnalyticsUserAgentString(AnalyticsAction.Record), - }) - ) - .catch(e => logger.warn('Failed to flush events', e)); + }); + }) + .catch(e => { + logger.warn('Failed to flush events', e); + }); }; diff --git a/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts b/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts index c93a21ccacb..e273ec7ceda 100644 --- a/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts +++ b/packages/analytics/src/providers/pinpoint/apis/identifyUser.ts @@ -1,22 +1,25 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; import { - updateEndpoint, UpdateEndpointException, + updateEndpoint, } from '@aws-amplify/core/internals/providers/pinpoint'; -import { AnalyticsValidationErrorCode } from '../../../errors'; -import { getAnalyticsUserAgentString } from '../../../utils'; -import { IdentifyUserInput } from '../types'; -import { resolveConfig, resolveCredentials } from '../utils'; +import { AnalyticsAction } from '@aws-amplify/core/internals/utils'; +import { getAnalyticsUserAgentString } from '~/src/utils'; +import { + resolveConfig, + resolveCredentials, +} from '~/src/providers/pinpoint/utils'; +import { AnalyticsValidationErrorCode } from '~/src/errors'; +import { IdentifyUserInput } from '~/src/providers/pinpoint/types'; /** * Sends information about a user to Pinpoint. Sending user information allows you to associate a user to their user * profile and activities or actions in your application. Activity can be tracked across devices & platforms by using * the same `userId`. * - * @param {IdentifyUserParameters} params The input object used to construct requests sent to Pinpoint's UpdateEndpoint + * @param {IdentifyUserInput} params The input object used to construct requests sent to Pinpoint's UpdateEndpoint * API. * * @throws service: {@link UpdateEndpointException} - Thrown when the underlying Pinpoint service returns an error. diff --git a/packages/analytics/src/providers/pinpoint/apis/record.ts b/packages/analytics/src/providers/pinpoint/apis/record.ts index c9de36b02ee..5701f336337 100644 --- a/packages/analytics/src/providers/pinpoint/apis/record.ts +++ b/packages/analytics/src/providers/pinpoint/apis/record.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Hub, ConsoleLogger } from '@aws-amplify/core'; +import { ConsoleLogger, Hub } from '@aws-amplify/core'; import { AMPLIFY_SYMBOL, AnalyticsAction, @@ -10,13 +10,13 @@ import { record as recordCore } from '@aws-amplify/core/internals/providers/pinp import { AnalyticsValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/errors'; +import { getAnalyticsUserAgentString, isAnalyticsEnabled } from '~/src/utils'; +import { RecordInput } from '~/src/providers/pinpoint/types'; import { - getAnalyticsUserAgentString, - isAnalyticsEnabled, -} from '../../../utils'; -import { RecordInput } from '../types'; -import { resolveConfig, resolveCredentials } from '../utils'; + resolveConfig, + resolveCredentials, +} from '~/src/providers/pinpoint/utils'; const logger = new ConsoleLogger('Analytics'); @@ -54,6 +54,7 @@ export const record = (input: RecordInput): void => { if (!isAnalyticsEnabled()) { logger.debug('Analytics is disabled, event will not be recorded.'); + return; } @@ -65,7 +66,7 @@ export const record = (input: RecordInput): void => { 'analytics', { event: 'record', data: input, message: 'Recording Analytics event' }, 'Analytics', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); recordCore({ appId, diff --git a/packages/analytics/src/providers/pinpoint/types/inputs.ts b/packages/analytics/src/providers/pinpoint/types/inputs.ts index acb28b68c4a..5036a7d20fd 100644 --- a/packages/analytics/src/providers/pinpoint/types/inputs.ts +++ b/packages/analytics/src/providers/pinpoint/types/inputs.ts @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { PinpointAnalyticsEvent } from '@aws-amplify/core/internals/providers/pinpoint'; -import { IdentifyUserOptions } from './options'; import { AnalyticsConfigureAutoTrackInput, AnalyticsIdentifyUserInput, -} from '../../../types'; +} from '~/src/types'; + +import { IdentifyUserOptions } from './options'; /** * Input type for Pinpoint record API. diff --git a/packages/analytics/src/providers/pinpoint/utils/resolveConfig.ts b/packages/analytics/src/providers/pinpoint/utils/resolveConfig.ts index 28d60e258cf..8c509796618 100644 --- a/packages/analytics/src/providers/pinpoint/utils/resolveConfig.ts +++ b/packages/analytics/src/providers/pinpoint/utils/resolveConfig.ts @@ -5,7 +5,7 @@ import { Amplify } from '@aws-amplify/core'; import { AnalyticsValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/errors'; /** * @internal diff --git a/packages/analytics/src/providers/pinpoint/utils/resolveCredentials.ts b/packages/analytics/src/providers/pinpoint/utils/resolveCredentials.ts index f4310fb1363..49874e46198 100644 --- a/packages/analytics/src/providers/pinpoint/utils/resolveCredentials.ts +++ b/packages/analytics/src/providers/pinpoint/utils/resolveCredentials.ts @@ -5,7 +5,7 @@ import { fetchAuthSession } from '@aws-amplify/core'; import { AnalyticsValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/errors'; /** * @internal @@ -14,7 +14,8 @@ export const resolveCredentials = async () => { const { credentials, identityId } = await fetchAuthSession(); assertValidationError( !!credentials, - AnalyticsValidationErrorCode.NoCredentials + AnalyticsValidationErrorCode.NoCredentials, ); + return { credentials, identityId }; }; diff --git a/packages/analytics/src/trackers/EventTracker.ts b/packages/analytics/src/trackers/EventTracker.ts index 68823b68ad2..47964be291b 100644 --- a/packages/analytics/src/trackers/EventTracker.ts +++ b/packages/analytics/src/trackers/EventTracker.ts @@ -1,14 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { ConsoleLogger } from '@aws-amplify/core'; +import { isBrowser } from '@aws-amplify/core/internals/utils'; import { - EventTrackingOptions, DOMEvent, + EventTrackingOptions, TrackerEventRecorder, TrackerInterface, -} from '../types/trackers'; -import { ConsoleLogger } from '@aws-amplify/core'; -import { isBrowser } from '@aws-amplify/core/internals/utils'; +} from '~/src/types/trackers'; const DEFAULT_EVENTS = ['click'] as DOMEvent[]; const DEFAULT_SELECTOR_PREFIX = 'data-amplify-analytics-'; @@ -23,7 +23,7 @@ export class EventTracker implements TrackerInterface { constructor( eventRecorder: TrackerEventRecorder, - options?: EventTrackingOptions + options?: EventTrackingOptions, ) { this.options = {}; this.trackerActive = false; @@ -35,7 +35,7 @@ export class EventTracker implements TrackerInterface { public configure( eventRecorder: TrackerEventRecorder, - options?: EventTrackingOptions + options?: EventTrackingOptions, ) { this.eventRecorder = eventRecorder; @@ -120,7 +120,7 @@ export class EventTracker implements TrackerInterface { target: `${target.localName} with id ${target.id}`, }, this.options.attributes, - elementAttributes + elementAttributes, ); logger.debug('Recording automatically tracked DOM event', { diff --git a/packages/analytics/src/trackers/PageViewTracker.ts b/packages/analytics/src/trackers/PageViewTracker.ts index 1278ee9e4df..672eac69357 100644 --- a/packages/analytics/src/trackers/PageViewTracker.ts +++ b/packages/analytics/src/trackers/PageViewTracker.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { ConsoleLogger } from '@aws-amplify/core'; +import { isBrowser } from '@aws-amplify/core/internals/utils'; import { PageViewTrackingOptions, TrackerEventRecorder, TrackerInterface, -} from '../types/trackers'; -import { ConsoleLogger } from '@aws-amplify/core'; -import { isBrowser } from '@aws-amplify/core/internals/utils'; +} from '~/src/types/trackers'; const logger = new ConsoleLogger('PageViewTracker'); @@ -32,7 +32,7 @@ export class PageViewTracker implements TrackerInterface { constructor( eventRecorder: TrackerEventRecorder, - options?: PageViewTrackingOptions + options?: PageViewTrackingOptions, ) { this.options = {}; this.trackerActive = true; @@ -45,7 +45,7 @@ export class PageViewTracker implements TrackerInterface { public configure( eventRecorder: TrackerEventRecorder, - options?: PageViewTrackingOptions + options?: PageViewTrackingOptions, ) { this.eventRecorder = eventRecorder; @@ -95,20 +95,14 @@ export class PageViewTracker implements TrackerInterface { // Configure proxies on History APIs this.pushStateProxy = Proxy.revocable(window.history.pushState, { apply: (target, thisArg, args) => { - const proxiedResult = target.apply(thisArg, args as any); - + target.apply(thisArg, args as any); this.handleLocationChange(); - - return proxiedResult; }, }); this.replaceStateProxy = Proxy.revocable(window.history.replaceState, { apply: (target, thisArg, args) => { - const proxiedResult = target.apply(thisArg, args as any); - + target.apply(thisArg, args as any); this.handleLocationChange(); - - return proxiedResult; }, }); @@ -139,7 +133,7 @@ export class PageViewTracker implements TrackerInterface { { url: currentUrl, }, - this.options.attributes + this.options.attributes, ); logger.debug('Recording automatically tracked page view event', { diff --git a/packages/analytics/src/trackers/SessionTracker.native.ts b/packages/analytics/src/trackers/SessionTracker.native.ts index 5958ffe1ecd..e3ce6e72073 100644 --- a/packages/analytics/src/trackers/SessionTracker.native.ts +++ b/packages/analytics/src/trackers/SessionTracker.native.ts @@ -2,17 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import { - sessionListener, - SessionState, SESSION_START_EVENT, SESSION_STOP_EVENT, + SessionState, + sessionListener, } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; import { SessionTrackingOptions, TrackerEventRecorder, TrackerInterface, -} from '../types/trackers'; +} from '~/src/types/trackers'; const logger = new ConsoleLogger('SessionTracker'); @@ -24,7 +24,7 @@ export class SessionTracker implements TrackerInterface { constructor( eventRecorder: TrackerEventRecorder, - options?: SessionTrackingOptions + options?: SessionTrackingOptions, ) { this.options = {}; this.eventRecorder = eventRecorder; @@ -37,7 +37,7 @@ export class SessionTracker implements TrackerInterface { public configure( eventRecorder: TrackerEventRecorder, - options?: SessionTrackingOptions + options?: SessionTrackingOptions, ) { this.eventRecorder = eventRecorder; @@ -53,7 +53,7 @@ export class SessionTracker implements TrackerInterface { if (!this.sessionTrackingActive) { sessionListener.addStateChangeListener( this.handleStateChange, - !this.initialEventSent + !this.initialEventSent, ); this.sessionTrackingActive = true; diff --git a/packages/analytics/src/trackers/SessionTracker.ts b/packages/analytics/src/trackers/SessionTracker.ts index feac38eb59a..d0540117544 100644 --- a/packages/analytics/src/trackers/SessionTracker.ts +++ b/packages/analytics/src/trackers/SessionTracker.ts @@ -2,17 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import { - sessionListener, - SessionState, SESSION_START_EVENT, SESSION_STOP_EVENT, + SessionState, + sessionListener, } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; import { SessionTrackingOptions, TrackerEventRecorder, TrackerInterface, -} from '../types/trackers'; +} from '~/src/types/trackers'; const logger = new ConsoleLogger('SessionTracker'); @@ -24,7 +24,7 @@ export class SessionTracker implements TrackerInterface { constructor( eventRecorder: TrackerEventRecorder, - options?: SessionTrackingOptions + options?: SessionTrackingOptions, ) { this.options = {}; this.eventRecorder = eventRecorder; @@ -37,7 +37,7 @@ export class SessionTracker implements TrackerInterface { public configure( eventRecorder: TrackerEventRecorder, - options?: SessionTrackingOptions + options?: SessionTrackingOptions, ) { this.eventRecorder = eventRecorder; @@ -53,7 +53,7 @@ export class SessionTracker implements TrackerInterface { if (!this.sessionTrackingActive) { sessionListener.addStateChangeListener( this.handleStateChange, - !this.initialEventSent + !this.initialEventSent, ); this.sessionTrackingActive = true; diff --git a/packages/analytics/src/types/inputs.ts b/packages/analytics/src/types/inputs.ts index 6f3911ad52b..4ddf06dde35 100644 --- a/packages/analytics/src/types/inputs.ts +++ b/packages/analytics/src/types/inputs.ts @@ -2,19 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 import { UserProfile } from '@aws-amplify/core'; -import { AnalyticsServiceOptions } from './options'; + import { - SessionTrackingOptions, - PageViewTrackingOptions, EventTrackingOptions, + PageViewTrackingOptions, + SessionTrackingOptions, } from './trackers'; +import { AnalyticsServiceOptions } from './options'; /** * Input type for `identifyUser`. */ -export type AnalyticsIdentifyUserInput< +export interface AnalyticsIdentifyUserInput< ServiceOptions extends AnalyticsServiceOptions = AnalyticsServiceOptions, -> = { +> { /** * A User ID associated to the current device. */ @@ -29,7 +30,7 @@ export type AnalyticsIdentifyUserInput< * Options to be passed to the API. */ options?: ServiceOptions; -}; +} /** * Input type for `configureAutoTrack`. diff --git a/packages/analytics/src/types/kinesis.ts b/packages/analytics/src/types/kinesis.ts index eb09b36f729..974b980728a 100644 --- a/packages/analytics/src/types/kinesis.ts +++ b/packages/analytics/src/types/kinesis.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type KinesisStream = { +export interface KinesisStream { region: string; streamName: string; -}; +} export type KinesisShard = KinesisStream & { partitionKey: string; diff --git a/packages/analytics/src/types/trackers.ts b/packages/analytics/src/types/trackers.ts index 3d5b24294ba..5c769b9f200 100644 --- a/packages/analytics/src/types/trackers.ts +++ b/packages/analytics/src/types/trackers.ts @@ -1,34 +1,34 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type SessionTrackingOptions = { +export type TrackerAttributes = Record; + +export type DOMEvent = keyof GlobalEventHandlersEventMap; + +export interface SessionTrackingOptions { attributes?: TrackerAttributes; -}; +} -export type PageViewTrackingOptions = { +export interface PageViewTrackingOptions { attributes?: TrackerAttributes; eventName?: string; - urlProvider?: () => string; + urlProvider?(): string; appType?: 'multiPage' | 'singlePage'; -}; +} -export type EventTrackingOptions = { +export interface EventTrackingOptions { attributes?: TrackerAttributes; events?: DOMEvent[]; selectorPrefix?: string; -}; +} export type TrackerType = 'event' | 'pageView' | 'session'; -export type TrackerAttributes = Record; - export type TrackerEventRecorder = ( eventName: string, - attributes: TrackerAttributes + attributes: TrackerAttributes, ) => void; -export type DOMEvent = keyof GlobalEventHandlersEventMap; - export interface TrackerInterface { configure(eventRecorder: TrackerEventRecorder, options?: object): void; cleanup(): void; diff --git a/packages/analytics/src/utils/eventBuffer/EventBuffer.ts b/packages/analytics/src/utils/eventBuffer/EventBuffer.ts index c71ffd15855..6c3b56ccf8b 100644 --- a/packages/analytics/src/utils/eventBuffer/EventBuffer.ts +++ b/packages/analytics/src/utils/eventBuffer/EventBuffer.ts @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { ConsoleLogger } from '@aws-amplify/core'; -import { EventBufferConfig, IAnalyticsClient } from './'; + +import { EventBufferConfig, IAnalyticsClient } from './types'; const logger = new ConsoleLogger('EventBuffer'); @@ -15,7 +16,7 @@ export class EventBuffer { constructor( config: EventBufferConfig, - getAnalyticsClient: () => IAnalyticsClient + getAnalyticsClient: () => IAnalyticsClient, ) { this.list = []; this.config = config; @@ -27,7 +28,7 @@ export class EventBuffer { for (const event of events) { if (this.list.length + 1 > this.config.bufferSize) { logger.debug( - `Exceed ${typeof event} event buffer limits, event dropped` + `Exceed ${typeof event} event buffer limits, event dropped`, ); continue; } @@ -69,16 +70,15 @@ export class EventBuffer { }, flushInterval); } - private submitEvents(count: number): Promise { + private async submitEvents(count: number): Promise { const events = this.head(count); if (events.length === 0) { return Promise.resolve(); } - return this.getAnalyticsClient()(events).then(result => { - if (result.length > 0) { - this.insertAtBeginning(...result); - } - }); + const result = await this.getAnalyticsClient()(events); + if (result.length > 0) { + this.insertAtBeginning(...result); + } } } diff --git a/packages/analytics/src/utils/eventBuffer/types.ts b/packages/analytics/src/utils/eventBuffer/types.ts index 0907a175389..994b089a928 100644 --- a/packages/analytics/src/utils/eventBuffer/types.ts +++ b/packages/analytics/src/utils/eventBuffer/types.ts @@ -1,12 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export interface IAnalyticsClient { - (events: T[]): Promise; -} +export type IAnalyticsClient = (events: T[]) => Promise; -export type EventBufferConfig = { +export interface EventBufferConfig { flushSize: number; flushInterval: number; bufferSize: number; -}; +} diff --git a/packages/analytics/src/utils/groupBy.ts b/packages/analytics/src/utils/groupBy.ts index c11dd6f225d..e4e153792ca 100644 --- a/packages/analytics/src/utils/groupBy.ts +++ b/packages/analytics/src/utils/groupBy.ts @@ -3,13 +3,11 @@ export const groupBy = ( getGroupId: (x: T) => string, - list: T[] + list: T[], ): Record => { - return list.reduce( - (result, current) => { - const groupId = getGroupId(current); - return { ...result, [groupId]: [...(result[groupId] ?? []), current] }; - }, - {} as Record - ); + return list.reduce((result, current) => { + const groupId = getGroupId(current); + + return { ...result, [groupId]: [...(result[groupId] ?? []), current] }; + }, {} as Record); }; diff --git a/packages/analytics/src/utils/resolveCredentials.ts b/packages/analytics/src/utils/resolveCredentials.ts index 53047419f45..2367e4def4c 100644 --- a/packages/analytics/src/utils/resolveCredentials.ts +++ b/packages/analytics/src/utils/resolveCredentials.ts @@ -1,14 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AnalyticsValidationErrorCode, assertValidationError } from '../errors'; import { fetchAuthSession } from '@aws-amplify/core'; +import { + AnalyticsValidationErrorCode, + assertValidationError, +} from '~/src/errors'; export const resolveCredentials = async () => { const { credentials, identityId } = await fetchAuthSession(); assertValidationError( !!credentials, - AnalyticsValidationErrorCode.NoCredentials + AnalyticsValidationErrorCode.NoCredentials, ); + return { credentials, identityId }; }; diff --git a/packages/analytics/src/utils/trackerConfigHelpers.native.ts b/packages/analytics/src/utils/trackerConfigHelpers.native.ts index eace1d7a550..e2c85c61957 100644 --- a/packages/analytics/src/utils/trackerConfigHelpers.native.ts +++ b/packages/analytics/src/utils/trackerConfigHelpers.native.ts @@ -1,18 +1,21 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AnalyticsValidationErrorCode, assertValidationError } from '../errors'; -import { AnalyticsConfigureAutoTrackInput } from '../types'; +import { + AnalyticsValidationErrorCode, + assertValidationError, +} from '~/src/errors'; +import { AnalyticsConfigureAutoTrackInput } from '~/src/types'; /** * Validates tracker configuration. */ export const validateTrackerConfiguration = ( - input: AnalyticsConfigureAutoTrackInput + input: AnalyticsConfigureAutoTrackInput, ) => { // React Native only supports session tracking assertValidationError( input.type === 'session', - AnalyticsValidationErrorCode.UnsupportedPlatform + AnalyticsValidationErrorCode.UnsupportedPlatform, ); }; diff --git a/packages/analytics/src/utils/trackerConfigHelpers.ts b/packages/analytics/src/utils/trackerConfigHelpers.ts index af70bef49fe..bd398c09349 100644 --- a/packages/analytics/src/utils/trackerConfigHelpers.ts +++ b/packages/analytics/src/utils/trackerConfigHelpers.ts @@ -1,19 +1,22 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AnalyticsValidationErrorCode, assertValidationError } from '../errors'; -import { AnalyticsConfigureAutoTrackInput } from '../types'; +import { + AnalyticsValidationErrorCode, + assertValidationError, +} from '~/src/errors'; +import { AnalyticsConfigureAutoTrackInput } from '~/src/types'; /** * Validates tracker configuration. */ export const validateTrackerConfiguration = ( - input: AnalyticsConfigureAutoTrackInput + input: AnalyticsConfigureAutoTrackInput, ) => { assertValidationError( input.type === 'event' || input.type === 'pageView' || input.type === 'session', - AnalyticsValidationErrorCode.InvalidTracker + AnalyticsValidationErrorCode.InvalidTracker, ); }; diff --git a/packages/analytics/src/utils/trackerHelpers.ts b/packages/analytics/src/utils/trackerHelpers.ts index 026daa67f42..863bab8000a 100644 --- a/packages/analytics/src/utils/trackerHelpers.ts +++ b/packages/analytics/src/utils/trackerHelpers.ts @@ -2,13 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { - TrackerAttributes, TrackerEventRecorder, TrackerInterface, TrackerType, -} from '../types/trackers'; -import { EventTracker, PageViewTracker, SessionTracker } from '../trackers'; -import { ConfigureAutoTrackInput } from '../providers/pinpoint'; +} from '~/src/types/trackers'; +import { EventTracker, PageViewTracker, SessionTracker } from '~/src/trackers'; +import { ConfigureAutoTrackInput } from '~/src/providers/pinpoint/types'; /** * Updates a provider's trackers as appropriate for the provided auto-track configuration. @@ -19,7 +18,7 @@ import { ConfigureAutoTrackInput } from '../providers/pinpoint'; export const updateProviderTrackers = ( input: ConfigureAutoTrackInput, providerEventRecorder: TrackerEventRecorder, - providerTrackers: Partial> + providerTrackers: Partial>, ) => { let trackerInstance; const trackerType = input.type; diff --git a/packages/analytics/src/utils/userAgent.ts b/packages/analytics/src/utils/userAgent.ts index c49e641667a..91f9db5f6cd 100644 --- a/packages/analytics/src/utils/userAgent.ts +++ b/packages/analytics/src/utils/userAgent.ts @@ -3,8 +3,8 @@ import { AnalyticsAction, Category, - getAmplifyUserAgentObject, getAmplifyUserAgent, + getAmplifyUserAgentObject, } from '@aws-amplify/core/internals/utils'; import { UserAgent } from '@aws-sdk/types'; diff --git a/packages/analytics/tsconfig.build.json b/packages/analytics/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/analytics/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/analytics/tsconfig.json b/packages/analytics/tsconfig.json index f907c964821..cc4ae13d539 100644 --- a/packages/analytics/tsconfig.json +++ b/packages/analytics/tsconfig.json @@ -1,9 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/analytics/tsconfig.test.json b/packages/analytics/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/analytics/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/analytics/tsconfig.watch.json b/packages/analytics/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/analytics/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/analytics/tslint.json b/packages/analytics/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/analytics/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/api-graphql/.eslintrc.js b/packages/api-graphql/.eslintrc.js new file mode 100644 index 00000000000..7d4cf55e19e --- /dev/null +++ b/packages/api-graphql/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/api-graphql', + }, + ], + }, +}; diff --git a/packages/api-graphql/__tests__/fixtures/modeled/amplifyconfiguration.js b/packages/api-graphql/__tests__/fixtures/modeled/amplifyconfiguration.js index b9262629e6b..f3cecc820c9 100644 --- a/packages/api-graphql/__tests__/fixtures/modeled/amplifyconfiguration.js +++ b/packages/api-graphql/__tests__/fixtures/modeled/amplifyconfiguration.js @@ -633,4 +633,4 @@ const amplifyConfig = { nonModels: {}, }, }; -export default amplifyConfig; +module.exports = amplifyConfig; diff --git a/packages/api-graphql/jest.config.js b/packages/api-graphql/jest.config.js deleted file mode 100644 index 5a542c861b5..00000000000 --- a/packages/api-graphql/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 61, - functions: 71, - lines: 75, - statements: 76, - }, - }, -}; diff --git a/packages/api-graphql/jest.config.mjs b/packages/api-graphql/jest.config.mjs new file mode 100644 index 00000000000..913d301839e --- /dev/null +++ b/packages/api-graphql/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 61, + functions: 71, + lines: 75, + statements: 76, + }, + }, +}); + +export default jestConfig; diff --git a/packages/api-graphql/package.json b/packages/api-graphql/package.json index 16bb21cd862..5dfd30c2e50 100644 --- a/packages/api-graphql/package.json +++ b/packages/api-graphql/package.json @@ -14,19 +14,20 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:watch": "tslint 'src/**/*.ts' && jest -w 1 --watch", "build-with-test": "npm test && npm build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 70.0" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 70.0" }, "exports": { ".": { @@ -74,8 +75,7 @@ "devDependencies": { "@aws-amplify/data-schema": "^0.12.11", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "files": [ "dist/cjs", diff --git a/packages/api-graphql/src/GraphQLAPI.ts b/packages/api-graphql/src/GraphQLAPI.ts index 31699d6981b..fbd0bd62aa3 100644 --- a/packages/api-graphql/src/GraphQLAPI.ts +++ b/packages/api-graphql/src/GraphQLAPI.ts @@ -1,16 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; -import { Category, ApiAction } from '@aws-amplify/core/internals/utils'; +import { ApiAction, Category } from '@aws-amplify/core/internals/utils'; +import { Observable } from 'rxjs'; +import { CustomHeaders } from '@aws-amplify/data-schema-types'; + import { GraphQLOptions, GraphQLResult } from './types'; import { InternalGraphQLAPIClass } from './internals/InternalGraphQLAPI'; -import { CustomHeaders } from '@aws-amplify/data-schema-types'; -import { Observable } from 'rxjs'; export const graphqlOperation = ( query: any, variables = {}, - authToken?: string + authToken?: string, ) => ({ query, variables, @@ -35,7 +36,7 @@ export class GraphQLAPIClass extends InternalGraphQLAPIClass { graphql( amplify: AmplifyClassV6 | (() => Promise), options: GraphQLOptions, - additionalHeaders?: CustomHeaders + additionalHeaders?: CustomHeaders, ): Observable> | Promise> { return super.graphql(amplify, options, additionalHeaders, { category: Category.API, diff --git a/packages/api-graphql/src/Providers/AWSAppSyncRealTimeProvider/index.ts b/packages/api-graphql/src/Providers/AWSAppSyncRealTimeProvider/index.ts index f4d5fdb8a4f..8e6cb415ab6 100644 --- a/packages/api-graphql/src/Providers/AWSAppSyncRealTimeProvider/index.ts +++ b/packages/api-graphql/src/Providers/AWSAppSyncRealTimeProvider/index.ts @@ -1,56 +1,55 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observable, SubscriptionLike } from 'rxjs'; +import { CustomHeaders, RequestOptions } from '@aws-amplify/data-schema-types'; import { GraphQLError } from 'graphql'; import { - Hub, - fetchAuthSession, ConsoleLogger, + Hub, HubPayload, + fetchAuthSession, } from '@aws-amplify/core'; import { signRequest } from '@aws-amplify/core/internals/aws-client-utils'; import { - base64Encoder, - GraphQLAuthMode, + AmplifyUrl, CustomUserAgentDetails, + DocumentType, + GraphQLAuthMode, NonRetryableError, USER_AGENT_HEADER, + amplifyUuid, + base64Encoder, getAmplifyUserAgent, isNonRetryableError, jitteredExponentialRetry, - DocumentType, - amplifyUuid, - AmplifyUrl, } from '@aws-amplify/core/internals/utils'; - import { CONTROL_MSG, ConnectionState, PubSubContentObserver, -} from '../../types/PubSub'; +} from '~/src/types/PubSub'; import { AMPLIFY_SYMBOL, AWS_APPSYNC_REALTIME_HEADERS, CONNECTION_INIT_TIMEOUT, - DEFAULT_KEEP_ALIVE_TIMEOUT, + CONNECTION_STATE_CHANGE, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT, + DEFAULT_KEEP_ALIVE_TIMEOUT, MAX_DELAY_MS, MESSAGE_TYPES, NON_RETRYABLE_CODES, SOCKET_STATUS, START_ACK_TIMEOUT, SUBSCRIPTION_STATUS, - CONNECTION_STATE_CHANGE, -} from '../constants'; +} from '~/src/Providers/constants'; import { - ConnectionStateMonitor, CONNECTION_CHANGE, -} from '../../utils/ConnectionStateMonitor'; + ConnectionStateMonitor, +} from '~/src/utils/ConnectionStateMonitor'; import { ReconnectEvent, ReconnectionMonitor, -} from '../../utils/ReconnectionMonitor'; -import { CustomHeaders, RequestOptions } from '@aws-amplify/data-schema-types'; +} from '~/src/utils/ReconnectionMonitor'; const logger = new ConsoleLogger('AWSAppSyncRealTimeProvider'); @@ -58,38 +57,38 @@ const dispatchApiEvent = (payload: HubPayload) => { Hub.dispatch('api', payload, 'PubSub', AMPLIFY_SYMBOL); }; -export type ObserverQuery = { +export interface ObserverQuery { observer: PubSubContentObserver; query: string; variables: Record; subscriptionState: SUBSCRIPTION_STATUS; - subscriptionReadyCallback?: Function; - subscriptionFailedCallback?: Function; + subscriptionReadyCallback?(...args: any[]): unknown; + subscriptionFailedCallback?(...args: any[]): unknown; startAckTimeoutId?: ReturnType; -}; +} const standardDomainPattern = - /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com(?:\.cn)?\/graphql$/i; + /^https:\/\/\w{26}\.appsync-api\.\w{2}(?:(?:-\w{2,})+)-\d\.amazonaws.com(?:\.cn)?\/graphql$/i; const customDomainPath = '/realtime'; -type DataObject = { +interface DataObject extends Record { data: Record; -}; +} -type DataPayload = { +interface DataPayload { id: string; payload: DataObject; type: string; -}; +} -type ParsedMessagePayload = { +interface ParsedMessagePayload { type: string; payload: { connectionTimeoutMs: number; errors?: [{ errorType: string; errorCode: number }]; }; -}; +} export interface AWSAppSyncRealTimeProviderOptions { appSyncGraphqlEndpoint?: string; @@ -98,7 +97,9 @@ export interface AWSAppSyncRealTimeProviderOptions { variables?: Record; apiKey?: string; region?: string; - libraryConfigHeaders?: () => {} | (() => Promise<{}>); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types + libraryConfigHeaders?(): {} | (() => Promise<{}>); additionalHeaders?: CustomHeaders; additionalCustomHeaders?: Record; authToken?: string; @@ -117,14 +118,18 @@ export class AWSAppSyncRealTimeProvider { private keepAliveTimeoutId?: ReturnType; private keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT; private keepAliveAlertTimeoutId?: ReturnType; - private subscriptionObserverMap: Map = new Map(); - private promiseArray: Array<{ res: Function; rej: Function }> = []; + private subscriptionObserverMap = new Map(); + private promiseArray: { + res(...args: any[]): void; + rej(...args: any[]): void; + }[] = []; + private connectionState: ConnectionState | undefined; private readonly connectionStateMonitor = new ConnectionStateMonitor(); private readonly reconnectionMonitor = new ReconnectionMonitor(); private connectionStateMonitorSubscription: SubscriptionLike; - constructor(options: AWSAppSyncRealTimeProviderOptions = {}) { + constructor() { // Monitor the connection state and pass changes along to Hub this.connectionStateMonitorSubscription = this.connectionStateMonitor.connectionStateObservable.subscribe( @@ -158,7 +163,7 @@ export class AWSAppSyncRealTimeProvider { ) { this.reconnectionMonitor.record(ReconnectEvent.HALT_RECONNECT); } - } + }, ); } @@ -191,7 +196,7 @@ export class AWSAppSyncRealTimeProvider { subscribe( options?: AWSAppSyncRealTimeProviderOptions, - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ): Observable> { const { appSyncGraphqlEndpoint, @@ -211,7 +216,7 @@ export class AWSAppSyncRealTimeProvider { errors: [ { ...new GraphQLError( - `Subscribe only available for AWS AppSync endpoint` + `Subscribe only available for AWS AppSync endpoint`, ), }, ], @@ -242,7 +247,7 @@ export class AWSAppSyncRealTimeProvider { customUserAgentDetails, }).catch(err => { logger.debug( - `${CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR}: ${err}` + `${CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR}: ${err}`, ); this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED); @@ -253,11 +258,9 @@ export class AWSAppSyncRealTimeProvider { } }; - let reconnectSubscription: SubscriptionLike; - // Add an observable to the reconnection list to manage reconnection for this subscription - reconnectSubscription = new Observable(observer => { - this.reconnectionMonitor.addObserver(observer); + const reconnectSubscription = new Observable(reconnectionObserver => { + this.reconnectionMonitor.addObserver(reconnectionObserver); }).subscribe(() => { startSubscription(); }); @@ -397,6 +400,7 @@ export class AWSAppSyncRealTimeProvider { }); } catch (err: any) { this._logStartSubscriptionError(subscriptionId, observer, err); + return; } @@ -416,6 +420,8 @@ export class AWSAppSyncRealTimeProvider { subscriptionReadyCallback, subscriptionFailedCallback, startAckTimeoutId: setTimeout(() => { + // TODO(eslint): remove this linter suppression with refactor. + // eslint-disable-next-line no-useless-call this._timeoutStartSubscriptionAck.call(this, subscriptionId); }, START_ACK_TIMEOUT), }); @@ -428,13 +434,17 @@ export class AWSAppSyncRealTimeProvider { private _logStartSubscriptionError( subscriptionId: string, observer: PubSubContentObserver, - err: { message?: string } + err: { message?: string }, ) { logger.debug({ err }); const message = String(err.message ?? ''); // Resolving to give the state observer time to propogate the update + // TODO(eslint): remove this linter suppression. + // eslint-disable-next-line promise/catch-or-return, @typescript-eslint/no-confusing-void-expression Promise.resolve( - this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED) + // TODO(eslint): remove this linter suppression. + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression + this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED), ); // Capture the error only when the network didn't cause disruption @@ -447,7 +457,7 @@ export class AWSAppSyncRealTimeProvider { errors: [ { ...new GraphQLError( - `${CONTROL_MSG.CONNECTION_FAILED}: ${message}` + `${CONTROL_MSG.CONNECTION_FAILED}: ${message}`, ), }, ], @@ -474,16 +484,20 @@ export class AWSAppSyncRealTimeProvider { const { subscriptionState } = subscriptionObserver; // This in case unsubscribe is invoked before sending start subscription message if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) { - return new Promise((res, rej) => { - const { observer, subscriptionState, variables, query } = - subscriptionObserver; + return new Promise((resolve, reject) => { + const { + observer, + subscriptionState: subscriptionStateWhenPending, + variables, + query, + } = subscriptionObserver; this.subscriptionObserverMap.set(subscriptionId, { observer, - subscriptionState, + subscriptionState: subscriptionStateWhenPending, variables, query, - subscriptionReadyCallback: res, - subscriptionFailedCallback: rej, + subscriptionReadyCallback: resolve, + subscriptionFailedCallback: reject, }); }); } @@ -526,6 +540,7 @@ export class AWSAppSyncRealTimeProvider { if (!this.awsRealTimeSocket) { this.socketStatus = SOCKET_STATUS.CLOSED; + return; } @@ -558,7 +573,7 @@ export class AWSAppSyncRealTimeProvider { return; } logger.debug( - `subscription message from AWS AppSync RealTime: ${message.data}` + `subscription message from AWS AppSync RealTime: ${message.data}`, ); const { id = '', @@ -582,12 +597,13 @@ export class AWSAppSyncRealTimeProvider { } else { logger.debug(`observer not found for id: ${id}`); } + return; } if (type === MESSAGE_TYPES.GQL_START_ACK) { logger.debug( - `subscription ready for ${JSON.stringify({ query, variables })}` + `subscription ready for ${JSON.stringify({ query, variables })}`, ); if (typeof subscriptionReadyCallback === 'function') { subscriptionReadyCallback(); @@ -611,7 +627,7 @@ export class AWSAppSyncRealTimeProvider { }); } this.connectionStateMonitor.record( - CONNECTION_CHANGE.CONNECTION_ESTABLISHED + CONNECTION_CHANGE.CONNECTION_ESTABLISHED, ); return; @@ -621,14 +637,14 @@ export class AWSAppSyncRealTimeProvider { if (this.keepAliveTimeoutId) clearTimeout(this.keepAliveTimeoutId); if (this.keepAliveAlertTimeoutId) clearTimeout(this.keepAliveAlertTimeoutId); - this.keepAliveTimeoutId = setTimeout( - () => this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT), - this.keepAliveTimeout - ); + this.keepAliveTimeoutId = setTimeout(() => { + this._errorDisconnect(CONTROL_MSG.TIMEOUT_DISCONNECT); + }, this.keepAliveTimeout); this.keepAliveAlertTimeoutId = setTimeout(() => { this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE_MISSED); }, DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT); this.connectionStateMonitor.record(CONNECTION_CHANGE.KEEP_ALIVE); + return; } @@ -646,14 +662,14 @@ export class AWSAppSyncRealTimeProvider { }); logger.debug( - `${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}` + `${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`, ); observer.error({ errors: [ { ...new GraphQLError( - `${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}` + `${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`, ), }, ], @@ -697,7 +713,7 @@ export class AWSAppSyncRealTimeProvider { this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED); logger.debug( 'timeoutStartSubscription', - JSON.stringify({ query, variables }) + JSON.stringify({ query, variables }), ); } } @@ -712,8 +728,11 @@ export class AWSAppSyncRealTimeProvider { if (this.socketStatus === SOCKET_STATUS.READY) { return; } - return new Promise(async (res, rej) => { - this.promiseArray.push({ res, rej }); + + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + this.promiseArray.push({ res: resolve, rej: reject }); if (this.socketStatus === SOCKET_STATUS.CLOSED) { try { @@ -765,7 +784,9 @@ export class AWSAppSyncRealTimeProvider { this.promiseArray = []; } catch (err) { logger.debug('Connection exited with', err); - this.promiseArray.forEach(({ rej }) => rej(err)); + this.promiseArray.forEach(({ rej }) => { + rej(err); + }); this.promiseArray = []; if ( this.awsRealTimeSocket && @@ -785,7 +806,7 @@ export class AWSAppSyncRealTimeProvider { await jitteredExponentialRetry( this._initializeHandshake.bind(this), [awsRealTimeUrl], - MAX_DELAY_MS + MAX_DELAY_MS, ); } @@ -795,23 +816,24 @@ export class AWSAppSyncRealTimeProvider { // Step 1: connect websocket try { await (() => { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { const newSocket = this.getNewWebSocket(awsRealTimeUrl, 'graphql-ws'); newSocket.onerror = () => { logger.debug(`WebSocket connection error`); }; newSocket.onclose = () => { - rej(new Error('Connection handshake error')); + reject(new Error('Connection handshake error')); }; newSocket.onopen = () => { this.awsRealTimeSocket = newSocket; - return res(); + + resolve(); }; }); })(); // Step 2: wait for ack from AWS AppSyncReaTime after sending init await (() => { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { if (this.awsRealTimeSocket) { let ackOk = false; this.awsRealTimeSocket.onerror = error => { @@ -819,7 +841,7 @@ export class AWSAppSyncRealTimeProvider { }; this.awsRealTimeSocket.onclose = event => { logger.debug(`WebSocket closed ${event.reason}`); - rej(new Error(JSON.stringify(event))); + reject(new Error(JSON.stringify(event))); }; this.awsRealTimeSocket.onmessage = (message: MessageEvent) => { @@ -827,7 +849,7 @@ export class AWSAppSyncRealTimeProvider { return; } logger.debug( - `subscription message from AWS AppSyncRealTime: ${message.data} ` + `subscription message from AWS AppSyncRealTime: ${message.data} `, ); const data = JSON.parse(message.data) as ParsedMessagePayload; const { @@ -851,7 +873,8 @@ export class AWSAppSyncRealTimeProvider { this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED); }; } - res('Cool, connected to AWS AppSyncRealTime'); + resolve('Cool, connected to AWS AppSyncRealTime'); + return; } @@ -861,8 +884,9 @@ export class AWSAppSyncRealTimeProvider { errors: [{ errorType = '', errorCode = 0 } = {}] = [], } = {}, } = data; - - rej({ errorType, errorCode }); + // TODO(eslint): remove this linter suppression. + // eslint-disable-next-line prefer-promise-reject-errors + reject({ errorType, errorCode }); } }; @@ -871,20 +895,22 @@ export class AWSAppSyncRealTimeProvider { }; this.awsRealTimeSocket.send(JSON.stringify(gqlInit)); - const checkAckOk = (ackOk: boolean) => { - if (!ackOk) { + const checkAckOk = (ackOkBeingChecked: boolean) => { + if (!ackOkBeingChecked) { this.connectionStateMonitor.record( - CONNECTION_CHANGE.CONNECTION_FAILED + CONNECTION_CHANGE.CONNECTION_FAILED, ); - rej( + reject( new Error( - `Connection timeout: ack from AWSAppSyncRealTime was not received after ${CONNECTION_INIT_TIMEOUT} ms` - ) + `Connection timeout: ack from AWSAppSyncRealTime was not received after ${CONNECTION_INIT_TIMEOUT} ms`, + ), ); } }; - setTimeout(() => checkAckOk(ackOk), CONNECTION_INIT_TIMEOUT); + setTimeout(() => { + checkAckOk(ackOk); + }, CONNECTION_INIT_TIMEOUT); } }); })(); @@ -916,7 +942,12 @@ export class AWSAppSyncRealTimeProvider { Record | undefined > { const headerHandler: { - [key in GraphQLAuthMode]: (arg0: AWSAppSyncRealTimeAuthInput) => {}; + [key in GraphQLAuthMode]: ( + arg0: AWSAppSyncRealTimeAuthInput, + ) => + | Promise | undefined> + | Record + | undefined; } = { apiKey: this._awsRealTimeApiKeyHeader.bind(this), iam: this._awsRealTimeIAMHeader.bind(this), @@ -928,6 +959,7 @@ export class AWSAppSyncRealTimeProvider { if (!authenticationType || !headerHandler[authenticationType]) { logger.debug(`Authentication type ${authenticationType} not supported`); + return undefined; } else { const handler = headerHandler[authenticationType]; @@ -969,7 +1001,7 @@ export class AWSAppSyncRealTimeProvider { host, }: AWSAppSyncRealTimeAuthInput) { const dt = new Date(); - const dtStr = dt.toISOString().replace(/[:\-]|\.\d{3}/g, ''); + const dtStr = dt.toISOString().replace(/[:-]|\.\d{3}/g, ''); return { host, @@ -998,7 +1030,7 @@ export class AWSAppSyncRealTimeProvider { headers: { ...AWS_APPSYNC_REALTIME_HEADERS }, }; - const signed_params = signRequest( + const signedParams = signRequest( { headers: request.headers, method: request.method, @@ -1010,9 +1042,10 @@ export class AWSAppSyncRealTimeProvider { credentials: creds!, signingRegion: endpointInfo.region!, signingService: endpointInfo.service, - } + }, ); - return signed_params.headers; + + return signedParams.headers; } private _customAuthHeader({ @@ -1024,7 +1057,7 @@ export class AWSAppSyncRealTimeProvider { * the headers that are returned by that function will already have been * provided before this function is called. */ - if (!additionalCustomHeaders?.['Authorization']) { + if (!additionalCustomHeaders?.Authorization) { throw new Error('No auth token specified'); } diff --git a/packages/api-graphql/src/internals/APIClient.ts b/packages/api-graphql/src/internals/APIClient.ts index 17b75319c05..4d0aeb5d34c 100644 --- a/packages/api-graphql/src/internals/APIClient.ts +++ b/packages/api-graphql/src/internals/APIClient.ts @@ -1,14 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { resolveOwnerFields } from '../utils/resolveOwnerFields'; +import { resolveOwnerFields } from '~/src/utils/resolveOwnerFields'; import { + AssociationBelongsTo, + AssociationHasOne, GraphQLAuthMode, - ModelIntrospectionSchema, ModelFieldType, + ModelIntrospectionSchema, SchemaModel, SchemaModels, - AssociationHasOne, - AssociationBelongsTo, } from '@aws-amplify/core/internals/utils'; import { AuthModeParams, @@ -16,22 +16,21 @@ import { ListArgs, QueryArgs, V6Client, - V6ClientSSRCookies, V6ClientSSRRequest, __authMode, __authToken, __headers, -} from '../types'; +} from '~/src/types'; import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core'; import { CustomHeaders } from '@aws-amplify/data-schema-types'; -type LazyLoadOptions = { +interface LazyLoadOptions { authMode?: GraphQLAuthMode; authToken?: string | undefined; limit?: number | undefined; nextToken?: string | undefined | null; headers?: CustomHeaders | undefined; -}; +} const connectionType = { HAS_ONE: 'HAS_ONE', @@ -51,11 +50,13 @@ export const flattenItems = (obj: Record): Record => { if (typeof value === 'object' && !Array.isArray(value) && value !== null) { if (value.items !== undefined) { res[prop] = value.items.map((item: Record) => - flattenItems(item) + flattenItems(item), ); + return; } res[prop] = flattenItems(value); + return; } @@ -73,7 +74,7 @@ export function initializeModel( modelIntrospection: ModelIntrospectionSchema, authMode: GraphQLAuthMode | undefined, authToken: string | undefined, - context = false + context = false, ): any[] { const introModel = modelIntrospection.models[modelName]; const introModelFields = introModel.fields; @@ -110,25 +111,30 @@ export function initializeModel( let targetNames: string[] = []; if (modelField.association && 'targetNames' in modelField.association) { - targetNames = modelField.association.targetNames; + const { + association: { targetNames: associationTargetNames }, + } = modelField; + targetNames = associationTargetNames; } switch (relationType) { case connectionType.HAS_ONE: - case connectionType.BELONGS_TO: + case connectionType.BELONGS_TO: { const sortKeyValues = relatedModelSKFieldNames.reduce( + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line array-callback-return (acc: Record, curVal) => { if (record[curVal]) { return (acc[curVal] = record[curVal]); } }, - {} + {}, ); if (context) { initializedRelationalFields[fieldName] = ( contextSpec: AmplifyServer.ContextSpec, - options?: LazyLoadOptions + options?: LazyLoadOptions, ) => { if (record[targetNames[0]]) { return ( @@ -142,14 +148,15 @@ export function initializeModel( { authMode: options?.authMode || authMode, authToken: options?.authToken || authToken, - } + }, ); } + return undefined; }; } else { initializedRelationalFields[fieldName] = ( - options?: LazyLoadOptions + options?: LazyLoadOptions, ) => { if (record[targetNames[0]]) { return (client as V6Client>).models[ @@ -162,15 +169,17 @@ export function initializeModel( { authMode: options?.authMode || authMode, authToken: options?.authToken || authToken, - } + }, ); } + return undefined; }; } break; - case connectionType.HAS_MANY: + } + case connectionType.HAS_MANY: { const parentPk = introModel.primaryKeyInfo.primaryKeyFieldName; const parentSK = introModel.primaryKeyInfo.sortKeyFieldNames; @@ -194,13 +203,13 @@ export function initializeModel( } return { [field]: { eq: record[parentSK[idx - 1]] } }; - } + }, ); if (context) { initializedRelationalFields[fieldName] = ( contextSpec: AmplifyServer.ContextSpec, - options?: LazyLoadOptions + options?: LazyLoadOptions, ) => { if (record[parentPk]) { return ( @@ -213,11 +222,12 @@ export function initializeModel( authToken: options?.authToken || authToken, }); } + return []; }; } else { initializedRelationalFields[fieldName] = ( - options?: LazyLoadOptions + options?: LazyLoadOptions, ) => { if (record[parentPk]) { return (client as V6Client>).models[ @@ -230,6 +240,7 @@ export function initializeModel( authToken: options?.authToken || authToken, }); } + return []; }; } @@ -244,13 +255,13 @@ export function initializeModel( } return { [field]: { eq: record[parentSK[idx - 1]] } }; - } + }, ); if (context) { initializedRelationalFields[fieldName] = ( contextSpec: AmplifyServer.ContextSpec, - options?: LazyLoadOptions + options?: LazyLoadOptions, ) => { if (record[parentPk]) { return ( @@ -263,11 +274,12 @@ export function initializeModel( authToken: options?.authToken || authToken, }); } + return []; }; } else { initializedRelationalFields[fieldName] = ( - options?: LazyLoadOptions + options?: LazyLoadOptions, ) => { if (record[parentPk]) { return (client as V6Client>).models[ @@ -280,11 +292,13 @@ export function initializeModel( authToken: options?.authToken || authToken, }); } + return []; }; } break; + } default: break; } @@ -307,9 +321,6 @@ export const graphQLOperationsInfo = { }; export type ModelOperation = keyof typeof graphQLOperationsInfo; -type OperationPrefix = - (typeof graphQLOperationsInfo)[ModelOperation]['operationPrefix']; - const graphQLDocumentsCache = new Map>(); const SELECTION_SET_WILDCARD = '*'; @@ -323,7 +334,7 @@ function defaultSelectionSetForModel(modelDefinition: SchemaModel): string[] { ({ type, name }) => (typeof type === 'string' || (typeof type === 'object' && typeof type?.enum === 'string')) && - name + name, ) .filter(Boolean); @@ -358,7 +369,7 @@ const FIELD_IR = ''; export function customSelectionSetToIR( modelDefinitions: SchemaModels, modelName: string, - selectionSet: string[] + selectionSet: string[], ): Record { const modelDefinition = modelDefinitions[modelName]; const { fields: modelFields } = modelDefinition; @@ -377,7 +388,7 @@ export function customSelectionSetToIR( const relatedModelDefinition = modelDefinitions[relatedModel]; - const selectionSet = + const selectionSetNested = nested === SELECTION_SET_WILDCARD ? defaultSelectionSetIR(relatedModelDefinition) : // if we have a path like 'field.anotherField' recursively build up selection set IR @@ -389,16 +400,18 @@ export function customSelectionSetToIR( const existing = resultObj[fieldName] || { items: {}, }; - const merged = { ...existing.items, ...selectionSet }; + const merged = { ...existing.items, ...selectionSetNested }; resultObj[fieldName] = { items: merged }; + return resultObj; } const existingItems = resultObj[fieldName] || {}; - const merged = { ...existingItems, ...selectionSet }; + const merged = { ...existingItems, ...selectionSetNested }; resultObj[fieldName] = merged; + return resultObj; } @@ -409,21 +422,23 @@ export function customSelectionSetToIR( } resultObj[fieldName] = FIELD_IR; + return resultObj; }, {}); } const defaultSelectionSetIR = (relatedModelDefinition: SchemaModel) => { const defaultSelectionSet = defaultSelectionSetForModel( - relatedModelDefinition + relatedModelDefinition, ); const reduced = defaultSelectionSet.reduce( (acc: Record, curVal) => { acc[curVal] = FIELD_IR; + return acc; }, - {} + {}, ); return reduced; @@ -445,7 +460,7 @@ const defaultSelectionSetIR = (relatedModelDefinition: SchemaModel) => { * `'id comments { items { post { id } } }'` */ export function selectionSetIRToString( - obj: Record + obj: Record, ): string { const res: string[] = []; @@ -461,7 +476,7 @@ export function selectionSetIRToString( '{', selectionSetIRToString(value.items), '}', - '}' + '}', ); } else { res.push(fieldName, '{', selectionSetIRToString(value), '}'); @@ -475,7 +490,7 @@ export function selectionSetIRToString( export function generateSelectionSet( modelDefinitions: SchemaModels, modelName: string, - selectionSet?: string[] + selectionSet?: string[], ) { const modelDefinition = modelDefinitions[modelName]; @@ -486,7 +501,7 @@ export function generateSelectionSet( const selSetIr = customSelectionSetToIR( modelDefinitions, modelName, - selectionSet + selectionSet, ); const selSetString = selectionSetIRToString(selSetIr); @@ -497,7 +512,7 @@ export function generateGraphQLDocument( modelDefinitions: SchemaModels, modelName: string, modelOperation: ModelOperation, - listArgs?: ListArgs + listArgs?: ListArgs, ): string { const modelDefinition = modelDefinitions[modelName]; @@ -534,7 +549,7 @@ export function generateGraphQLDocument( const selectionSetFields = generateSelectionSet( modelDefinitions, modelName, - selectionSet + selectionSet, ); switch (modelOperation) { @@ -549,6 +564,8 @@ export function generateGraphQLDocument( }${name}Input!`, }); graphQLOperationType ?? (graphQLOperationType = 'mutation'); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-fallthrough case 'READ': graphQLArguments ?? (graphQLArguments = isCustomPrimaryKey @@ -558,12 +575,14 @@ export function generateGraphQLDocument( return acc; }, - {} + {}, ) : { [primaryKeyFieldName]: `${fields[primaryKeyFieldName].type}!`, }); graphQLSelectionSet ?? (graphQLSelectionSet = selectionSetFields); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-fallthrough case 'LIST': graphQLArguments ?? (graphQLArguments = { @@ -574,6 +593,8 @@ export function generateGraphQLDocument( graphQLOperationType ?? (graphQLOperationType = 'query'); graphQLSelectionSet ?? (graphQLSelectionSet = `items { ${selectionSetFields} } nextToken __typename`); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-fallthrough case 'ONCREATE': case 'ONUPDATE': case 'ONDELETE': @@ -587,20 +608,20 @@ export function generateGraphQLDocument( case 'OBSERVE_QUERY': default: throw new Error( - 'Internal error: Attempted to generate graphql document for observeQuery. Please report this error.' + 'Internal error: Attempted to generate graphql document for observeQuery. Please report this error.', ); } const graphQLDocument = `${graphQLOperationType}${ graphQLArguments ? `(${Object.entries(graphQLArguments).map( - ([fieldName, type]) => `\$${fieldName}: ${type}` + ([fieldName, type]) => `$${fieldName}: ${type}`, )})` : '' } { ${graphQLFieldName}${ graphQLArguments ? `(${Object.keys(graphQLArguments).map( - fieldName => `${fieldName}: \$${fieldName}` + fieldName => `${fieldName}: $${fieldName}`, )})` : '' } { ${graphQLSelectionSet} } }`; @@ -614,7 +635,7 @@ export function buildGraphQLVariables( modelDefinition: SchemaModel, operation: ModelOperation, arg: QueryArgs | undefined, - modelIntrospection: ModelIntrospectionSchema + modelIntrospection: ModelIntrospectionSchema, ): object { const { fields, @@ -642,12 +663,16 @@ export function buildGraphQLVariables( input: arg ? Object.fromEntries( Object.entries( - normalizeMutationInput(arg, modelDefinition, modelIntrospection) + normalizeMutationInput( + arg, + modelDefinition, + modelIntrospection, + ), ).filter(([fieldName]) => { const { isReadOnly } = fields[fieldName]; return !isReadOnly; - }) + }), ) : {}, }; @@ -663,7 +688,7 @@ export function buildGraphQLVariables( return acc; }, - {} + {}, ) : { [primaryKeyFieldName]: arg[primaryKeyFieldName] }; } @@ -692,12 +717,13 @@ export function buildGraphQLVariables( break; case 'OBSERVE_QUERY': throw new Error( - 'Internal error: Attempted to build variables for observeQuery. Please report this error.' + 'Internal error: Attempted to build variables for observeQuery. Please report this error.', ); break; - default: + default: { const exhaustiveCheck: never = operation; throw new Error(`Unhandled operation case: ${exhaustiveCheck}`); + } } return variables; @@ -720,7 +746,7 @@ export function buildGraphQLVariables( export function normalizeMutationInput( mutationInput: QueryArgs, model: SchemaModel, - modelIntrospection: ModelIntrospectionSchema + modelIntrospection: ModelIntrospectionSchema, ): QueryArgs { const { fields } = model; @@ -781,7 +807,7 @@ export function normalizeMutationInput( */ export function authModeParams( client: ClientWithModels, - options: AuthModeParams = {} + options: AuthModeParams = {}, ): AuthModeParams { return { authMode: options.authMode || client[__authMode], @@ -797,7 +823,7 @@ export function authModeParams( */ export function getCustomHeaders( client: V6Client | ClientWithModels, - requestHeaders?: CustomHeaders + requestHeaders?: CustomHeaders, ): CustomHeaders { let headers: CustomHeaders = client[__headers] || {}; diff --git a/packages/api-graphql/src/internals/InternalGraphQLAPI.ts b/packages/api-graphql/src/internals/InternalGraphQLAPI.ts index 41dab97903c..93a339ac89b 100644 --- a/packages/api-graphql/src/internals/InternalGraphQLAPI.ts +++ b/packages/api-graphql/src/internals/InternalGraphQLAPI.ts @@ -2,45 +2,43 @@ // SPDX-License-Identifier: Apache-2.0 import { DocumentNode, - OperationDefinitionNode, - print, - parse, GraphQLError, + OperationDefinitionNode, OperationTypeNode, + parse, + print, } from 'graphql'; import { Observable, catchError } from 'rxjs'; -import { AmplifyClassV6, ConsoleLogger } from '@aws-amplify/core'; +import { AmplifyClassV6 } from '@aws-amplify/core'; import { - GraphQLAuthMode, + AmplifyUrl, CustomUserAgentDetails, + GraphQLAuthMode, getAmplifyUserAgent, - AmplifyUrl, } from '@aws-amplify/core/internals/utils'; import { GraphQLAuthError, - GraphQLResult, GraphQLOperation, GraphQLOptions, -} from '../types'; + GraphQLResult, +} from '~/src/types'; import { isCancelError as isCancelErrorREST } from '@aws-amplify/api-rest'; import { - post, cancel as cancelREST, + post, updateRequestToBeCancellable, } from '@aws-amplify/api-rest/internals'; -import { AWSAppSyncRealTimeProvider } from '../Providers/AWSAppSyncRealTimeProvider'; import { CustomHeaders, RequestOptions } from '@aws-amplify/data-schema-types'; -import { resolveConfig, resolveLibraryOptions } from '../utils'; -import { repackageUnauthError } from '../utils/errors/repackageAuthError'; +import { AWSAppSyncRealTimeProvider } from '~/src/Providers/AWSAppSyncRealTimeProvider'; +import { resolveConfig, resolveLibraryOptions } from '~/src/utils'; +import { repackageUnauthError } from '~/src/utils/errors/repackageAuthError'; const USER_AGENT_HEADER = 'x-amz-user-agent'; -const logger = new ConsoleLogger('GraphQLAPI'); - const isAmplifyInstance = ( amplify: | AmplifyClassV6 - | ((fn: (amplify: any) => Promise) => Promise) + | ((fn: (amplify: any) => Promise) => Promise), ): amplify is AmplifyClassV6 => { return typeof amplify !== 'function'; }; @@ -68,13 +66,9 @@ export class InternalGraphQLAPIClass { private async _headerBasedAuth( amplify: AmplifyClassV6, authMode: GraphQLAuthMode, - additionalHeaders: Record = {} + additionalHeaders: Record = {}, ) { - const { - region: region, - endpoint: appSyncGraphqlEndpoint, - apiKey, - } = resolveConfig(amplify); + const { apiKey } = resolveConfig(amplify); let headers = {}; @@ -87,18 +81,17 @@ export class InternalGraphQLAPIClass { 'X-Api-Key': apiKey, }; break; - case 'iam': + case 'iam': { const session = await amplify.Auth.fetchAuthSession(); if (session.credentials === undefined) { throw new Error(GraphQLAuthError.NO_CREDENTIALS); } break; + } case 'oidc': - case 'userPool': + case 'userPool': { try { - let token; - - token = ( + const token = ( await amplify.Auth.fetchAuthSession() ).tokens?.accessToken.toString(); @@ -112,6 +105,7 @@ export class InternalGraphQLAPIClass { throw new Error(GraphQLAuthError.NO_CURRENT_USER); } break; + } case 'lambda': if ( typeof additionalHeaders === 'object' && @@ -139,8 +133,7 @@ export class InternalGraphQLAPIClass { */ getGraphqlOperationType(operation: GraphQLOperation): OperationTypeNode { const doc = parse(operation); - const definitions = - doc.definitions as ReadonlyArray; + const definitions = doc.definitions as readonly OperationDefinitionNode[]; const [{ operation: operationType }] = definitions; return operationType; @@ -159,7 +152,7 @@ export class InternalGraphQLAPIClass { | ((fn: (amplify: any) => Promise) => Promise), { query: paramQuery, variables = {}, authMode, authToken }: GraphQLOptions, additionalHeaders?: CustomHeaders, - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ): Observable> | Promise> { const query = typeof paramQuery === 'string' @@ -167,7 +160,7 @@ export class InternalGraphQLAPIClass { : parse(print(paramQuery)); const [operationDef = {}] = query.definitions.filter( - def => def.kind === 'OperationDefinition' + def => def.kind === 'OperationDefinition', ); const { operation: operationType } = operationDef as OperationDefinitionNode; @@ -176,7 +169,7 @@ export class InternalGraphQLAPIClass { switch (operationType) { case 'query': - case 'mutation': + case 'mutation': { const abortController = new AbortController(); let responsePromise: Promise>; @@ -188,7 +181,7 @@ export class InternalGraphQLAPIClass { headers, abortController, customUserAgentDetails, - authToken + authToken, ); } else { const wrapper = (amplifyInstance: AmplifyClassV6) => @@ -198,7 +191,7 @@ export class InternalGraphQLAPIClass { headers, abortController, customUserAgentDetails, - authToken + authToken, ); responsePromise = amplify(wrapper) as unknown as Promise< @@ -208,16 +201,18 @@ export class InternalGraphQLAPIClass { this._api.updateRequestToBeCancellable( responsePromise, - abortController + abortController, ); + return responsePromise; + } case 'subscription': return this._graphqlSubscribe( amplify as AmplifyClassV6, { query, variables, authMode }, headers, customUserAgentDetails, - authToken + authToken, ); default: throw new Error(`invalid operation type: ${operationType}`); @@ -230,10 +225,10 @@ export class InternalGraphQLAPIClass { additionalHeaders: CustomHeaders = {}, abortController: AbortController, customUserAgentDetails?: CustomUserAgentDetails, - authToken?: string + authToken?: string, ): Promise> { const { - region: region, + region, endpoint: appSyncGraphqlEndpoint, customEndpoint, customEndpointRegion, @@ -263,7 +258,7 @@ export class InternalGraphQLAPIClass { const requestOptions: RequestOptions = { method: 'POST', url: new AmplifyUrl( - customEndpoint || appSyncGraphqlEndpoint || '' + customEndpoint || appSyncGraphqlEndpoint || '', ).toString(), queryString: print(query as DocumentNode), }; @@ -287,7 +282,7 @@ export class InternalGraphQLAPIClass { (await this._headerBasedAuth( amplify, authMode!, - additionalCustomHeaders + additionalCustomHeaders, ))), /** * Custom endpoint headers. @@ -300,7 +295,7 @@ export class InternalGraphQLAPIClass { ? await this._headerBasedAuth( amplify, authMode!, - additionalCustomHeaders + additionalCustomHeaders, ) : {})) || {}), @@ -352,6 +347,8 @@ export class InternalGraphQLAPIClass { if (!endpoint) { const error = new GraphQLError('No graphql endpoint provided.'); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-throw-literal throw { data: {}, errors: [error], @@ -392,7 +389,7 @@ export class InternalGraphQLAPIClass { null, null, null, - err as any + err as any, ), ], }; @@ -430,7 +427,7 @@ export class InternalGraphQLAPIClass { { query, variables, authMode }: GraphQLOptions, additionalHeaders: CustomHeaders = {}, customUserAgentDetails?: CustomUserAgentDetails, - authToken?: string + authToken?: string, ): Observable { const config = resolveConfig(amplify); @@ -457,7 +454,7 @@ export class InternalGraphQLAPIClass { authToken, libraryConfigHeaders, }, - customUserAgentDetails + customUserAgentDetails, ) .pipe( catchError(e => { @@ -465,7 +462,7 @@ export class InternalGraphQLAPIClass { throw repackageUnauthError(e); } throw e; - }) + }), ); } } diff --git a/packages/api-graphql/src/internals/generateClient.ts b/packages/api-graphql/src/internals/generateClient.ts index 7bf63b93835..7a65b673daa 100644 --- a/packages/api-graphql/src/internals/generateClient.ts +++ b/packages/api-graphql/src/internals/generateClient.ts @@ -1,14 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { graphql, cancel, isCancelError } from './v6'; -import { generateModelsProperty } from './generateModelsProperty'; import { V6Client, __amplify, __authMode, __authToken, __headers, -} from '../types'; +} from '~/src/types'; + +import { cancel, graphql, isCancelError } from './v6'; +import { generateModelsProperty } from './generateModelsProperty'; import { ClientGenerationParams } from './types'; /** @@ -21,7 +22,7 @@ import { ClientGenerationParams } from './types'; * @returns */ export function generateClient = never>( - params: ClientGenerationParams + params: ClientGenerationParams, ): V6Client { const client = { [__amplify]: params.amplify, diff --git a/packages/api-graphql/src/internals/generateModelsProperty.ts b/packages/api-graphql/src/internals/generateModelsProperty.ts index ec7a9e009cf..8bf0c7aae08 100644 --- a/packages/api-graphql/src/internals/generateModelsProperty.ts +++ b/packages/api-graphql/src/internals/generateModelsProperty.ts @@ -1,19 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { ModelTypes } from '@aws-amplify/data-schema-types'; -import { graphQLOperationsInfo, ModelOperation } from './APIClient'; -import { ClientGenerationParams } from './types'; -import { V6Client, __authMode, __authToken } from '../types'; +import { V6Client } from '~/src/types'; +import { ModelIntrospectionSchema } from '@aws-amplify/core/internals/utils'; +import { ModelOperation, graphQLOperationsInfo } from './APIClient'; +import { ClientGenerationParams } from './types'; import { listFactory } from './operations/list'; import { getFactory } from './operations/get'; import { subscriptionFactory } from './operations/subscription'; import { observeQueryFactory } from './operations/observeQuery'; -import { ModelIntrospectionSchema } from '@aws-amplify/core/internals/utils'; export function generateModelsProperty = never>( client: V6Client>, - params: ClientGenerationParams + params: ClientGenerationParams, ): ModelTypes { const models = {} as any; const config = params.amplify.getConfig(); @@ -51,14 +51,14 @@ export function generateModelsProperty = never>( models[name][operationPrefix] = listFactory( client, modelIntrospection, - model + model, ); } else if (SUBSCRIPTION_OPS.includes(operation)) { models[name][operationPrefix] = subscriptionFactory( client, modelIntrospection, model, - operation + operation, ); } else if (operation === 'OBSERVE_QUERY') { models[name][operationPrefix] = observeQueryFactory(models, model); @@ -67,10 +67,10 @@ export function generateModelsProperty = never>( client, modelIntrospection, model, - operation + operation, ); } - } + }, ); } diff --git a/packages/api-graphql/src/internals/operations/get.ts b/packages/api-graphql/src/internals/operations/get.ts index 71b2d0c1e6c..00d2e9b2d37 100644 --- a/packages/api-graphql/src/internals/operations/get.ts +++ b/packages/api-graphql/src/internals/operations/get.ts @@ -2,14 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core'; import { - initializeModel, - generateGraphQLDocument, + ModelOperation, + authModeParams, buildGraphQLVariables, flattenItems, - authModeParams, - ModelOperation, + generateGraphQLDocument, getCustomHeaders, -} from '../APIClient'; + initializeModel, +} from '~/src/internals/APIClient'; import { AuthModeParams, ClientWithModels, @@ -19,7 +19,7 @@ import { QueryArgs, V6Client, V6ClientSSRRequest, -} from '../../types'; +} from '~/src/types'; import { ModelIntrospectionSchema, SchemaModel, @@ -30,12 +30,12 @@ export function getFactory( modelIntrospection: ModelIntrospectionSchema, model: SchemaModel, operation: ModelOperation, - useContext = false + useContext = false, ) { const getWithContext = async ( contextSpec: AmplifyServer.ContextSpec & GraphQLOptionsV6, arg?: any, - options?: any + options?: any, ) => { return _get( client, @@ -44,7 +44,7 @@ export function getFactory( arg, options, operation, - contextSpec + contextSpec, ); }; @@ -62,7 +62,7 @@ async function _get( arg: QueryArgs, options: AuthModeParams & ListArgs, operation: ModelOperation, - context?: AmplifyServer.ContextSpec + context?: AmplifyServer.ContextSpec, ) { const { name } = model; @@ -70,13 +70,13 @@ async function _get( modelIntrospection.models, name, operation, - options + options, ); const variables = buildGraphQLVariables( model, operation, arg, - modelIntrospection + modelIntrospection, ); try { @@ -92,7 +92,6 @@ async function _get( query, variables, }, - headers )) as GraphQLResult) : ((await (client as V6Client>).graphql( { @@ -100,7 +99,7 @@ async function _get( query, variables, }, - headers + headers, )) as GraphQLResult); // flatten response @@ -119,7 +118,7 @@ async function _get( modelIntrospection, auth.authMode, auth.authToken, - !!context + !!context, ); return { data: initialized, extensions }; diff --git a/packages/api-graphql/src/internals/operations/list.ts b/packages/api-graphql/src/internals/operations/list.ts index 4dad05e71ad..511e63d2f9b 100644 --- a/packages/api-graphql/src/internals/operations/list.ts +++ b/packages/api-graphql/src/internals/operations/list.ts @@ -2,21 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core'; import { - initializeModel, - generateGraphQLDocument, + authModeParams, buildGraphQLVariables, flattenItems, - authModeParams, + generateGraphQLDocument, getCustomHeaders, -} from '../APIClient'; + initializeModel, +} from '~/src/internals/APIClient'; import { AuthModeParams, ClientWithModels, + GraphQLResult, ListArgs, V6Client, V6ClientSSRRequest, - GraphQLResult, -} from '../../types'; +} from '~/src/types'; import { ModelIntrospectionSchema, SchemaModel, @@ -26,11 +26,11 @@ export function listFactory( client: ClientWithModels, modelIntrospection: ModelIntrospectionSchema, model: SchemaModel, - context = false + context = false, ) { const listWithContext = async ( contextSpec: AmplifyServer.ContextSpec, - args?: ListArgs + args?: ListArgs, ) => { return _list(client, modelIntrospection, model, args, contextSpec); }; @@ -47,7 +47,7 @@ async function _list( modelIntrospection: ModelIntrospectionSchema, model: SchemaModel, args?: ListArgs & AuthModeParams, - contextSpec?: AmplifyServer.ContextSpec + contextSpec?: AmplifyServer.ContextSpec, ) { const { name } = model; @@ -55,13 +55,13 @@ async function _list( modelIntrospection.models, name, 'LIST', - args + args, ); const variables = buildGraphQLVariables( model, 'LIST', args, - modelIntrospection + modelIntrospection, ); try { @@ -69,7 +69,7 @@ async function _list( const headers = getCustomHeaders(client, args?.headers); - const { data, extensions } = !!contextSpec + const { data, extensions } = contextSpec ? ((await (client as V6ClientSSRRequest>).graphql( contextSpec, { @@ -77,7 +77,7 @@ async function _list( query, variables, }, - headers + headers, )) as GraphQLResult) : ((await (client as V6Client>).graphql( { @@ -85,7 +85,7 @@ async function _list( query, variables, }, - headers + headers, )) as GraphQLResult); // flatten response @@ -110,7 +110,7 @@ async function _list( modelIntrospection, auth.authMode, auth.authToken, - !!contextSpec + !!contextSpec, ); return { diff --git a/packages/api-graphql/src/internals/operations/observeQuery.ts b/packages/api-graphql/src/internals/operations/observeQuery.ts index cf45c30bf06..0ec8cf98d59 100644 --- a/packages/api-graphql/src/internals/operations/observeQuery.ts +++ b/packages/api-graphql/src/internals/operations/observeQuery.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observable } from 'rxjs'; -import { findIndexByFields, resolvePKFields } from '../../utils'; +import { findIndexByFields, resolvePKFields } from '~/src/utils'; import { SchemaModel } from '@aws-amplify/core/internals/utils'; export function observeQueryFactory(models: any, model: SchemaModel) { @@ -124,6 +124,7 @@ export function observeQueryFactory(models: any, model: SchemaModel) { // switch the queue to write directly to the items collection receiveMessages = (...messages: typeof messageQueue) => { ingestMessages(messages); + return items.length; }; })(); diff --git a/packages/api-graphql/src/internals/operations/subscription.ts b/packages/api-graphql/src/internals/operations/subscription.ts index 912eb5caa4a..741e2836a11 100644 --- a/packages/api-graphql/src/internals/operations/subscription.ts +++ b/packages/api-graphql/src/internals/operations/subscription.ts @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { map } from 'rxjs'; -import { V6Client, GraphqlSubscriptionResult } from '../../types'; +import { GraphqlSubscriptionResult, V6Client } from '~/src/types'; import { - initializeModel, - generateGraphQLDocument, - buildGraphQLVariables, - authModeParams, ModelOperation, + authModeParams, + buildGraphQLVariables, + generateGraphQLDocument, getCustomHeaders, -} from '../APIClient'; + initializeModel, +} from '~/src/internals/APIClient'; import { ModelIntrospectionSchema, SchemaModel, @@ -20,7 +20,7 @@ export function subscriptionFactory( client: any, modelIntrospection: ModelIntrospectionSchema, model: SchemaModel, - operation: ModelOperation + operation: ModelOperation, ) { const { name } = model as any; @@ -28,13 +28,13 @@ export function subscriptionFactory( const query = generateGraphQLDocument( modelIntrospection.models, name, - operation + operation, ); const variables = buildGraphQLVariables( model, operation, args, - modelIntrospection + modelIntrospection, ); const auth = authModeParams(client, args); @@ -47,7 +47,7 @@ export function subscriptionFactory( query, variables, }, - headers + headers, ) as GraphqlSubscriptionResult; return observable.pipe( @@ -60,10 +60,11 @@ export function subscriptionFactory( [data], modelIntrospection, auth.authMode, - auth.authToken + auth.authToken, ); + return initialized; - }) + }), ); }; diff --git a/packages/api-graphql/src/internals/server/generateClientWithAmplifyInstance.ts b/packages/api-graphql/src/internals/server/generateClientWithAmplifyInstance.ts index e578a925d68..19f284a0766 100644 --- a/packages/api-graphql/src/internals/server/generateClientWithAmplifyInstance.ts +++ b/packages/api-graphql/src/internals/server/generateClientWithAmplifyInstance.ts @@ -1,17 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { graphql, cancel, isCancelError } from '..'; -import { generateModelsProperty } from './generateModelsProperty'; +import { cancel, graphql, isCancelError } from '~/src/internals'; import { + CommonPublicClientOptions, + ServerClientGenerationParams, + V6ClientSSRCookies, + V6ClientSSRRequest, __amplify, __authMode, __authToken, - V6ClientSSRRequest, - V6ClientSSRCookies, - ServerClientGenerationParams, - CommonPublicClientOptions, -} from '../../types'; +} from '~/src/types'; + +import { generateModelsProperty } from './generateModelsProperty'; /** * @private @@ -30,7 +31,7 @@ export function generateClientWithAmplifyInstance< | V6ClientSSRRequest | V6ClientSSRCookies = V6ClientSSRCookies, >( - params: ServerClientGenerationParams & CommonPublicClientOptions + params: ServerClientGenerationParams & CommonPublicClientOptions, ): ClientType { const client = { [__amplify]: params.amplify, diff --git a/packages/api-graphql/src/internals/server/generateModelsProperty.ts b/packages/api-graphql/src/internals/server/generateModelsProperty.ts index 7bc19fba940..88a74b89dfb 100644 --- a/packages/api-graphql/src/internals/server/generateModelsProperty.ts +++ b/packages/api-graphql/src/internals/server/generateModelsProperty.ts @@ -1,13 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { ModelTypes } from '@aws-amplify/data-schema-types'; -import { graphQLOperationsInfo, ModelOperation } from '../APIClient'; -import { ServerClientGenerationParams } from '../../types/'; -import { V6ClientSSRRequest, V6ClientSSRCookies } from '../../types'; +import { + ModelOperation, + graphQLOperationsInfo, +} from '~/src/internals/APIClient'; +import { + ServerClientGenerationParams, + V6ClientSSRCookies, + V6ClientSSRRequest, +} from '~/src/types'; import { ModelIntrospectionSchema } from '@aws-amplify/core/internals/utils'; - -import { listFactory } from '../operations/list'; -import { getFactory } from '../operations/get'; +import { listFactory } from '~/src/internals/operations/list'; +import { getFactory } from '~/src/internals/operations/get'; export function generateModelsProperty< _T extends Record = never, @@ -18,7 +23,7 @@ export function generateModelsProperty< >, >(client: ClientType, params: ServerClientGenerationParams): ClientType { const models = {} as any; - const config = params.config; + const { config } = params; const useContext = params.amplify === null; if (!config) { @@ -59,7 +64,7 @@ export function generateModelsProperty< client, modelIntrospection, model, - useContext + useContext, ); } else { models[name][operationPrefix] = getFactory( @@ -67,10 +72,10 @@ export function generateModelsProperty< modelIntrospection, model, operation, - useContext + useContext, ); } - } + }, ); } diff --git a/packages/api-graphql/src/internals/types.ts b/packages/api-graphql/src/internals/types.ts index ab2f4ec94b8..40154d778a1 100644 --- a/packages/api-graphql/src/internals/types.ts +++ b/packages/api-graphql/src/internals/types.ts @@ -16,8 +16,8 @@ export type ClientGenerationParams = { /** * Common options that can be used on public `generateClient()` interfaces. */ -export type CommonPublicClientOptions = { +export interface CommonPublicClientOptions { authMode?: GraphQLAuthMode; authToken?: string; headers?: CustomHeaders; -}; +} diff --git a/packages/api-graphql/src/internals/v6.ts b/packages/api-graphql/src/internals/v6.ts index 337758e4451..7c269ea06ee 100644 --- a/packages/api-graphql/src/internals/v6.ts +++ b/packages/api-graphql/src/internals/v6.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { GraphQLAPI } from '../GraphQLAPI'; +import { CustomHeaders } from '@aws-amplify/data-schema-types'; +import { GraphQLAPI } from '~/src/GraphQLAPI'; import { + GraphQLOptionsV6, + GraphQLResponseV6, + V6Client, __amplify, __authMode, __authToken, - V6Client, - GraphQLOptionsV6, - GraphQLResponseV6, -} from '../types'; -import { CustomHeaders } from '@aws-amplify/data-schema-types'; +} from '~/src/types'; /** * Invokes graphql operations against a graphql service, providing correct input and @@ -101,12 +101,12 @@ export function graphql< >( this: V6Client, options: GraphQLOptionsV6, - additionalHeaders?: CustomHeaders + additionalHeaders?: CustomHeaders, ): GraphQLResponseV6 { - // inject client-level auth + // inject client-level auth options.authMode = options.authMode || this[__authMode]; options.authToken = options.authToken || this[__authToken]; - + /** * The correctness of these typings depends on correct string branding or overrides. * Neither of these can actually be validated at runtime. Hence, we don't perform @@ -115,8 +115,9 @@ export function graphql< const result = GraphQLAPI.graphql( this[__amplify], options, - additionalHeaders + additionalHeaders, ); + return result as any; } @@ -128,7 +129,7 @@ export function graphql< export function cancel( this: V6Client, promise: Promise, - message?: string + message?: string, ): boolean { return GraphQLAPI.cancel(promise, message); } diff --git a/packages/api-graphql/src/server/generateClient.ts b/packages/api-graphql/src/server/generateClient.ts index ede4af9baef..871c4c10d32 100644 --- a/packages/api-graphql/src/server/generateClient.ts +++ b/packages/api-graphql/src/server/generateClient.ts @@ -5,7 +5,7 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { generateClientWithAmplifyInstance } from '../internals/server'; +import { generateClientWithAmplifyInstance } from '~/src/internals/server'; import { GenerateServerClientParams, GraphQLMethod, @@ -13,7 +13,7 @@ import { GraphQLOptionsV6, V6ClientSSRRequest, __amplify, -} from '../types'; +} from '~/src/types'; import { CustomHeaders } from '@aws-amplify/data-schema-types'; /** @@ -52,16 +52,18 @@ export function generateClient = never>({ const wrappedGraphql = ( contextSpec: AmplifyServer.ContextSpec, options: GraphQLOptionsV6, - additionalHeaders?: CustomHeaders + additionalHeaders?: CustomHeaders, ) => { const amplifyInstance = getAmplifyServerContext(contextSpec).amplify; + return prevGraphql.call( { [__amplify]: amplifyInstance }, options, - additionalHeaders as any + additionalHeaders as any, ); }; client.graphql = wrappedGraphql as unknown as GraphQLMethodSSR; + return client; } diff --git a/packages/api-graphql/src/types/PubSub.ts b/packages/api-graphql/src/types/PubSub.ts index a770e1f4b66..e7e0cc7dae3 100644 --- a/packages/api-graphql/src/types/PubSub.ts +++ b/packages/api-graphql/src/types/PubSub.ts @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observer } from 'rxjs'; + export interface SubscriptionObserver { closed: boolean; next(value: T): void; diff --git a/packages/api-graphql/src/types/index.ts b/packages/api-graphql/src/types/index.ts index 5b643086511..414ea87a630 100644 --- a/packages/api-graphql/src/types/index.ts +++ b/packages/api-graphql/src/types/index.ts @@ -1,17 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6, ResourcesConfig } from '@aws-amplify/core'; -import { ModelTypes, CustomHeaders } from '@aws-amplify/data-schema-types'; -import { Source, DocumentNode, GraphQLError } from 'graphql'; -export { OperationTypeNode } from 'graphql'; +import { CustomHeaders, ModelTypes } from '@aws-amplify/data-schema-types'; +import { DocumentNode, GraphQLError, Source } from 'graphql'; import { Observable } from 'rxjs'; - import { - GraphQLAuthMode, DocumentType, + GraphQLAuthMode, } from '@aws-amplify/core/internals/utils'; import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core'; +export { OperationTypeNode } from 'graphql'; + export { CONTROL_MSG, ConnectionState } from './PubSub'; export { SelectionSet } from '@aws-amplify/data-schema-types'; @@ -35,9 +35,7 @@ export interface GraphQLOptions { export interface GraphQLResult { data: T; errors?: GraphQLError[]; - extensions?: { - [key: string]: any; - }; + extensions?: Record; } // Opaque type used for determining the graphql query type @@ -69,6 +67,8 @@ export type GraphQLSubscription = T & { * * This util simply makes related model properties optional recursively. */ +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line @typescript-eslint/ban-types export type GraphQLReturnType = T extends {} ? { [K in keyof T]?: GraphQLReturnType; @@ -79,11 +79,11 @@ export type GraphQLReturnType = T extends {} * Describes a paged list result from AppSync, which can either * live at the top query or property (e.g., related model) level. */ -type PagedList = { +interface PagedList { __typename: TYPENAME; nextToken?: string | null | undefined; - items: Array; -}; + items: T[]; +} /** * Recursively looks through a result type and removes nulls and @@ -95,7 +95,9 @@ type PagedList = { */ type WithListsFixed = T extends PagedList ? PagedList, NAME> - : T extends {} + : // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types + T extends {} ? { [K in keyof T]: WithListsFixed; } @@ -164,9 +166,9 @@ export type GraphqlSubscriptionResult = Observable< * }) * ``` */ -export type GraphqlSubscriptionMessage = { +export interface GraphqlSubscriptionMessage { data: T; -}; +} export interface AWSAppSyncRealTimeProviderOptions { appSyncGraphqlEndpoint?: string; @@ -175,15 +177,17 @@ export interface AWSAppSyncRealTimeProviderOptions { variables?: Record; apiKey?: string; region?: string; - libraryConfigHeaders?: () => {} | (() => Promise<{}>); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types + graphql_headers?(): {} | (() => Promise<{}>); additionalHeaders?: CustomHeaders; } -export type AWSAppSyncRealTimeProvider = { +export interface AWSAppSyncRealTimeProvider { subscribe( - options?: AWSAppSyncRealTimeProviderOptions + options?: AWSAppSyncRealTimeProviderOptions, ): Observable>; -}; +} export enum GraphQLAuthError { NO_API_KEY = 'No api-key configured', @@ -252,17 +256,27 @@ export type GraphQLVariablesV6< export type GraphQLResponseV6< FALLBACK_TYPE = unknown, TYPED_GQL_STRING extends string = string, + // eslint-disable-next-line unused-imports/no-unused-vars > = TYPED_GQL_STRING extends GeneratedQuery ? Promise>> - : TYPED_GQL_STRING extends GeneratedMutation + : // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line unused-imports/no-unused-vars + TYPED_GQL_STRING extends GeneratedMutation ? Promise>> - : TYPED_GQL_STRING extends GeneratedSubscription + : // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line unused-imports/no-unused-vars + TYPED_GQL_STRING extends GeneratedSubscription ? GraphqlSubscriptionResult> - : FALLBACK_TYPE extends GraphQLQuery + : // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line unused-imports/no-unused-vars + FALLBACK_TYPE extends GraphQLQuery ? Promise> - : FALLBACK_TYPE extends GraphQLSubscription + : // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line unused-imports/no-unused-vars + FALLBACK_TYPE extends GraphQLSubscription ? GraphqlSubscriptionResult : FALLBACK_TYPE extends GraphQLOperationType< + // eslint-disable-next-line unused-imports/no-unused-vars infer IN, infer CUSTOM_OUT > @@ -287,10 +301,12 @@ export type GraphQLResponseV6< * }) * ``` */ -export type GraphQLOperationType = { +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line @typescript-eslint/ban-types +export interface GraphQLOperationType { variables: IN; result: OUT; -}; +} /** * Nominal type for branding generated graphql query operation strings with @@ -379,8 +395,8 @@ export type V6Client = never> = ExcludeNeverFields<{ [__authToken]?: string; [__headers]?: CustomHeaders; graphql: GraphQLMethod; - cancel: (promise: Promise, message?: string) => boolean; - isCancelError: (error: any) => boolean; + cancel(promise: Promise, message?: string): boolean; + isCancelError(error: any): boolean; models: ModelTypes; }>; @@ -391,8 +407,8 @@ export type V6ClientSSRRequest = never> = [__authToken]?: string; [__headers]?: CustomHeaders; graphql: GraphQLMethodSSR; - cancel: (promise: Promise, message?: string) => boolean; - isCancelError: (error: any) => boolean; + cancel(promise: Promise, message?: string): boolean; + isCancelError(error: any): boolean; models: ModelTypes; }>; @@ -403,8 +419,8 @@ export type V6ClientSSRCookies = never> = [__authToken]?: string; [__headers]?: CustomHeaders; graphql: GraphQLMethod; - cancel: (promise: Promise, message?: string) => boolean; - isCancelError: (error: any) => boolean; + cancel(promise: Promise, message?: string): boolean; + isCancelError(error: any): boolean; models: ModelTypes; }>; @@ -413,7 +429,7 @@ export type GraphQLMethod = < TYPED_GQL_STRING extends string = string, >( options: GraphQLOptionsV6, - additionalHeaders?: CustomHeaders | undefined + additionalHeaders?: CustomHeaders | undefined, ) => GraphQLResponseV6; export type GraphQLMethodSSR = < @@ -422,7 +438,7 @@ export type GraphQLMethodSSR = < >( contextSpec: AmplifyServer.ContextSpec, options: GraphQLOptionsV6, - additionalHeaders?: CustomHeaders | undefined + additionalHeaders?: CustomHeaders | undefined, ) => GraphQLResponseV6; /** @@ -430,30 +446,32 @@ export type GraphQLMethodSSR = < * * The knobs available for configuring `server/generateClient` internally. */ -export type ServerClientGenerationParams = { +export interface ServerClientGenerationParams { amplify: | null // null expected when used with `generateServerClient` // closure expected with `generateServerClientUsingCookies` | ((fn: (amplify: AmplifyClassV6) => Promise) => Promise); // global env-sourced config use for retrieving modelIntro config: ResourcesConfig; -}; +} export type QueryArgs = Record; -export type ListArgs = { +export interface ListArgs extends Record { selectionSet?: string[]; + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types filter?: {}; headers?: CustomHeaders; -}; +} -export type AuthModeParams = { +export interface AuthModeParams { authMode?: GraphQLAuthMode; authToken?: string; -}; +} -export type GenerateServerClientParams = { +export interface GenerateServerClientParams { config: ResourcesConfig; authMode?: GraphQLAuthMode; authToken?: string; -}; +} diff --git a/packages/api-graphql/src/utils/ConnectionStateMonitor.ts b/packages/api-graphql/src/utils/ConnectionStateMonitor.ts index fc26700978f..fccb7b748e0 100644 --- a/packages/api-graphql/src/utils/ConnectionStateMonitor.ts +++ b/packages/api-graphql/src/utils/ConnectionStateMonitor.ts @@ -1,19 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Observable, Observer, SubscriptionLike, map, filter } from 'rxjs'; -import { ConnectionState } from '../types/PubSub'; +import { Observable, Observer, SubscriptionLike, filter, map } from 'rxjs'; +import { ConnectionState } from '~/src/types/PubSub'; + import { ReachabilityMonitor } from './ReachabilityMonitor'; // Internal types for tracking different connection states type LinkedConnectionState = 'connected' | 'disconnected'; type LinkedHealthState = 'healthy' | 'unhealthy'; -type LinkedConnectionStates = { +interface LinkedConnectionStates { networkState: LinkedConnectionState; connectionState: LinkedConnectionState | 'connecting'; intendedConnectionState: LinkedConnectionState; keepAliveState: LinkedHealthState; -}; +} export const CONNECTION_CHANGE: { [key in @@ -53,6 +54,7 @@ export class ConnectionStateMonitor { private _linkedConnectionStateObserver: | Observer | undefined; + private _networkMonitoringSubscription?: SubscriptionLike; private _initialNetworkStateSubscription?: SubscriptionLike; @@ -69,10 +71,10 @@ export class ConnectionStateMonitor { this._initialNetworkStateSubscription = ReachabilityMonitor().subscribe( ({ online }) => { this.record( - online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE + online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE, ); this._initialNetworkStateSubscription?.unsubscribe(); - } + }, ); this._linkedConnectionStateObservable = @@ -94,9 +96,9 @@ export class ConnectionStateMonitor { this._networkMonitoringSubscription = ReachabilityMonitor().subscribe( ({ online }) => { this.record( - online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE + online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE, ); - } + }, ); } } @@ -126,14 +128,15 @@ export class ConnectionStateMonitor { .pipe( map(value => { return this.connectionStatesTranslator(value); - }) + }), ) .pipe( filter(current => { const toInclude = current !== previous; previous = current; + return toInclude; - }) + }), ); } @@ -196,6 +199,7 @@ export class ConnectionStateMonitor { // All remaining states directly correspond to the connection state if (connectionState === 'connecting') return ConnectionState.Connecting; if (connectionState === 'disconnected') return ConnectionState.Disconnected; + return ConnectionState.Connected; } } diff --git a/packages/api-graphql/src/utils/ReachabilityMonitor/index.native.ts b/packages/api-graphql/src/utils/ReachabilityMonitor/index.native.ts index 540d7da67ec..ef422376d9d 100644 --- a/packages/api-graphql/src/utils/ReachabilityMonitor/index.native.ts +++ b/packages/api-graphql/src/utils/ReachabilityMonitor/index.native.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Reachability } from '@aws-amplify/core/internals/utils'; -import { default as NetInfo } from '@react-native-community/netinfo'; +import NetInfo from '@react-native-community/netinfo'; export const ReachabilityMonitor = () => new Reachability().networkMonitor(NetInfo); diff --git a/packages/api-graphql/src/utils/ReachabilityMonitor/index.ts b/packages/api-graphql/src/utils/ReachabilityMonitor/index.ts index 1f17e311524..53f10cd91dc 100644 --- a/packages/api-graphql/src/utils/ReachabilityMonitor/index.ts +++ b/packages/api-graphql/src/utils/ReachabilityMonitor/index.ts @@ -1,4 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Reachability } from '@aws-amplify/core/internals/utils'; + export const ReachabilityMonitor = () => new Reachability().networkMonitor(); diff --git a/packages/api-graphql/src/utils/ReconnectionMonitor.ts b/packages/api-graphql/src/utils/ReconnectionMonitor.ts index 50ada9cf1b5..c750afbbdf8 100644 --- a/packages/api-graphql/src/utils/ReconnectionMonitor.ts +++ b/packages/api-graphql/src/utils/ReconnectionMonitor.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observer } from 'rxjs'; -import { RECONNECT_DELAY, RECONNECT_INTERVAL } from '../Providers/constants'; +import { RECONNECT_DELAY, RECONNECT_INTERVAL } from '~/src/Providers/constants'; export enum ReconnectEvent { START_RECONNECT = 'START_RECONNECT', diff --git a/packages/api-graphql/src/utils/errors/assertValidationError.ts b/packages/api-graphql/src/utils/errors/assertValidationError.ts index 2a93d6e8bb2..e7e5510fe5b 100644 --- a/packages/api-graphql/src/utils/errors/assertValidationError.ts +++ b/packages/api-graphql/src/utils/errors/assertValidationError.ts @@ -9,7 +9,7 @@ import { APIValidationErrorCode, validationErrorMap } from './validation'; */ export function assertValidationError( assertion: boolean, - name: APIValidationErrorCode + name: APIValidationErrorCode, ): asserts assertion { const { message, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/api-graphql/src/utils/errors/repackageAuthError.ts b/packages/api-graphql/src/utils/errors/repackageAuthError.ts index ba18b753550..e179c67436d 100644 --- a/packages/api-graphql/src/utils/errors/repackageAuthError.ts +++ b/packages/api-graphql/src/utils/errors/repackageAuthError.ts @@ -1,14 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - AmplifyErrorParams, - GraphQLAuthMode, -} from '@aws-amplify/core/internals/utils'; +import { AmplifyErrorParams } from '@aws-amplify/core/internals/utils'; -type ErrorObject = { +interface ErrorObject { errors: AmplifyErrorParams[]; -}; +} /** * Checks to see if the given response or subscription message contains an @@ -27,12 +24,13 @@ export function repackageUnauthError(content: T): T { } }); } + return content; } function isUnauthError(error: any): boolean { // Error pattern corresponding to appsync calls - if (error?.['originalError']?.['name']?.startsWith('UnauthorizedException')) { + if (error?.originalError?.name?.startsWith('UnauthorizedException')) { return true; } // Error pattern corresponding to appsync subscriptions @@ -42,5 +40,6 @@ function isUnauthError(error: any): boolean { ) { return true; } + return false; } diff --git a/packages/api-graphql/src/utils/findIndexByFields.ts b/packages/api-graphql/src/utils/findIndexByFields.ts index 6d342be7530..762a3fce850 100644 --- a/packages/api-graphql/src/utils/findIndexByFields.ts +++ b/packages/api-graphql/src/utils/findIndexByFields.ts @@ -11,16 +11,16 @@ export function findIndexByFields( needle: T, haystack: T[], - keyFields: Array + keyFields: (keyof T)[], ): number { const searchObject = Object.fromEntries( - keyFields.map(fieldName => [fieldName, needle[fieldName]]) + keyFields.map(fieldName => [fieldName, needle[fieldName]]), ); for (let i = 0; i < haystack.length; i++) { if ( Object.keys(searchObject).every( - k => searchObject[k] === (haystack[i] as any)[k] + k => searchObject[k] === (haystack[i] as any)[k], ) ) { return i; diff --git a/packages/api-graphql/src/utils/resolveConfig.ts b/packages/api-graphql/src/utils/resolveConfig.ts index 8662d07bc21..a3c5eaf656b 100644 --- a/packages/api-graphql/src/utils/resolveConfig.ts +++ b/packages/api-graphql/src/utils/resolveConfig.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6, ConsoleLogger } from '@aws-amplify/core'; + import { APIValidationErrorCode, assertValidationError } from './errors'; const logger = new ConsoleLogger('GraphQLAPI resolveConfig'); @@ -14,7 +15,7 @@ export const resolveConfig = (amplify: AmplifyClassV6) => { if (!config.API?.GraphQL) { logger.warn( - 'The API configuration is missing. This is likely due to Amplify.configure() not being called prior to generateClient().' + 'The API configuration is missing. This is likely due to Amplify.configure() not being called prior to generateClient().', ); } @@ -31,7 +32,7 @@ export const resolveConfig = (amplify: AmplifyClassV6) => { // assertValidationError(!!endpoint, APIValidationErrorCode.NoEndpoint); assertValidationError( !(!customEndpoint && customEndpointRegion), - APIValidationErrorCode.NoCustomEndpoint + APIValidationErrorCode.NoCustomEndpoint, ); return { diff --git a/packages/api-graphql/src/utils/resolveLibraryOptions.ts b/packages/api-graphql/src/utils/resolveLibraryOptions.ts index 82702f5c16a..465cef72e2d 100644 --- a/packages/api-graphql/src/utils/resolveLibraryOptions.ts +++ b/packages/api-graphql/src/utils/resolveLibraryOptions.ts @@ -9,5 +9,6 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; export const resolveLibraryOptions = (amplify: AmplifyClassV6) => { const headers = amplify.libraryOptions?.API?.GraphQL?.headers; const withCredentials = amplify.libraryOptions?.API?.GraphQL?.withCredentials; + return { headers, withCredentials }; }; diff --git a/packages/api-graphql/src/utils/resolveOwnerFields.ts b/packages/api-graphql/src/utils/resolveOwnerFields.ts index da916743069..e6baffbe515 100644 --- a/packages/api-graphql/src/utils/resolveOwnerFields.ts +++ b/packages/api-graphql/src/utils/resolveOwnerFields.ts @@ -9,20 +9,20 @@ type ModelIntrospectionSchema = Exclude< >; type Model = ModelIntrospectionSchema['models'][string]; -type AuthAttribute = { +interface AuthAttribute { type: 'auth'; properties: { rules: AuthRule[]; }; -}; +} /** * Only the portions of an Auth rule we care about. */ -type AuthRule = { +interface AuthRule { allow: string; ownerField?: string; -}; +} /** * Given an introspection schema model, returns all owner fields. @@ -41,6 +41,7 @@ export function resolveOwnerFields(model: Model): string[] { } } } + return Array.from(ownerFields); } @@ -55,11 +56,12 @@ function isAuthAttribute(attribute: any): attribute is AuthAttribute { if (attribute?.type === 'auth') { if (typeof attribute?.properties === 'object') { if (Array.isArray(attribute?.properties?.rules)) { - return (attribute?.properties?.rules as Array).every( - rule => !!rule.allow + return (attribute?.properties?.rules as any[]).every( + rule => !!rule.allow, ); } } } + return false; } diff --git a/packages/api-graphql/src/utils/resolvePKFields.ts b/packages/api-graphql/src/utils/resolvePKFields.ts index aad93f5b8d2..c00b5b94218 100644 --- a/packages/api-graphql/src/utils/resolvePKFields.ts +++ b/packages/api-graphql/src/utils/resolvePKFields.ts @@ -18,5 +18,6 @@ type SchemaModel = ModelIntrospectionSchema['models'][string]; */ export function resolvePKFields(model: SchemaModel) { const { primaryKeyFieldName, sortKeyFieldNames } = model.primaryKeyInfo; + return [primaryKeyFieldName, ...sortKeyFieldNames]; } diff --git a/packages/api-graphql/tsconfig.build.json b/packages/api-graphql/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/api-graphql/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/api-graphql/tsconfig.json b/packages/api-graphql/tsconfig.json index e477014534b..6b34e7c961c 100644 --- a/packages/api-graphql/tsconfig.json +++ b/packages/api-graphql/tsconfig.json @@ -1,11 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "skipLibCheck": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + }, + "strictNullChecks": true }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/api-graphql/tsconfig.test.json b/packages/api-graphql/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/api-graphql/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/api-graphql/tsconfig.watch.json b/packages/api-graphql/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/api-graphql/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/api-graphql/tslint.json b/packages/api-graphql/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/api-graphql/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/api-rest/.eslintrc.js b/packages/api-rest/.eslintrc.js new file mode 100644 index 00000000000..f9082998d0b --- /dev/null +++ b/packages/api-rest/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/api-rest', + }, + ], + }, +}; diff --git a/packages/api-rest/__tests__/apis/common/internalPost.test.ts b/packages/api-rest/__tests__/apis/common/internalPost.test.ts index 32b83ba53cd..349994cf78f 100644 --- a/packages/api-rest/__tests__/apis/common/internalPost.test.ts +++ b/packages/api-rest/__tests__/apis/common/internalPost.test.ts @@ -37,7 +37,7 @@ const successResponse = { }, }; const apiGatewayUrl = new URL( - 'https://123.execute-api.us-west-2.amazonaws.com' + 'https://123.execute-api.us-west-2.amazonaws.com', ); const credentials = { accessKeyId: 'accessKeyId', @@ -68,7 +68,7 @@ describe('internal post', () => { method: 'POST', headers: {}, }, - expect.objectContaining({ region: 'us-east-1', service: 'execute-api' }) + expect.objectContaining({ region: 'us-east-1', service: 'execute-api' }), ); }); @@ -87,7 +87,7 @@ describe('internal post', () => { method: 'POST', headers: {}, }, - expect.objectContaining({ region: 'us-west-2', service: 'lambda' }) + expect.objectContaining({ region: 'us-west-2', service: 'lambda' }), ); }); it('should call authenticatedHandler with empty signingServiceInfo', async () => { @@ -103,7 +103,7 @@ describe('internal post', () => { method: 'POST', headers: {}, }, - expect.objectContaining({ region: 'us-west-2', service: 'execute-api' }) + expect.objectContaining({ region: 'us-west-2', service: 'execute-api' }), ); }); @@ -124,7 +124,7 @@ describe('internal post', () => { }, body: '{"foo":"bar"}', }, - expect.anything() + expect.anything(), ); }); @@ -149,7 +149,7 @@ describe('internal post', () => { }), body: formData, }, - expect.anything() + expect.anything(), ); }); @@ -163,7 +163,7 @@ describe('internal post', () => { method: 'POST', headers: {}, }, - expect.anything() + expect.anything(), ); }); @@ -183,7 +183,7 @@ describe('internal post', () => { 'x-api-key': '123', }, }), - expect.anything() + expect.anything(), ); expect(mockAuthenticatedHandler).not.toHaveBeenCalled(); }); @@ -204,19 +204,19 @@ describe('internal post', () => { authorization: '123', }, }), - expect.anything() + expect.anything(), ); expect(mockAuthenticatedHandler).not.toHaveBeenCalled(); }); it('should abort request when cancel is called', async () => { expect.assertions(4); - let underLyingHandlerReject; + let underLyingHandlerReject: (reason?: any) => void; mockUnauthenticatedHandler.mockReset(); mockUnauthenticatedHandler.mockReturnValue( new Promise((_, reject) => { underLyingHandlerReject = reject; - }) + }), ); const abortController = new AbortController(); const promise = post(mockAmplifyInstance, { @@ -258,7 +258,7 @@ describe('internal post', () => { }, }; mockParseJsonError.mockResolvedValueOnce( - new RestApiError({ message: 'fooMessage', name: 'badRequest' }) + new RestApiError({ message: 'fooMessage', name: 'badRequest' }), ); mockUnauthenticatedHandler.mockResolvedValueOnce(errorResponse); try { diff --git a/packages/api-rest/__tests__/apis/common/publicApis.test.ts b/packages/api-rest/__tests__/apis/common/publicApis.test.ts index 82adcea15cf..39aaaed9d0a 100644 --- a/packages/api-rest/__tests__/apis/common/publicApis.test.ts +++ b/packages/api-rest/__tests__/apis/common/publicApis.test.ts @@ -102,7 +102,7 @@ describe('public APIs', () => { expect(mockAuthenticatedHandler).toHaveBeenCalledWith( { url: new URL( - 'https://123.execute-api.us-west-2.amazonaws.com/development/items' + 'https://123.execute-api.us-west-2.amazonaws.com/development/items', ), method, headers: {}, @@ -112,7 +112,7 @@ describe('public APIs', () => { region: 'us-west-2', service: 'execute-api', withCrossDomainCredentials: true, - }) + }), ); expect(response.headers).toEqual({ 'response-header': 'response-header-value', @@ -135,7 +135,7 @@ describe('public APIs', () => { expect(mockAuthenticatedHandler).toHaveBeenCalledWith( { url: new URL( - 'https://123.execute-api.us-west-2.amazonaws.com/development/items' + 'https://123.execute-api.us-west-2.amazonaws.com/development/items', ), method, headers: { @@ -146,7 +146,7 @@ describe('public APIs', () => { expect.objectContaining({ region: 'us-west-2', service: 'execute-api', - }) + }), ); }); @@ -163,7 +163,7 @@ describe('public APIs', () => { expect(mockAuthenticatedHandler).toHaveBeenCalledWith( { url: new URL( - 'https://123.execute-api.us-west-2.amazonaws.com/development/items' + 'https://123.execute-api.us-west-2.amazonaws.com/development/items', ), method, headers: { @@ -174,7 +174,7 @@ describe('public APIs', () => { expect.objectContaining({ region: 'us-west-2', service: 'execute-api', - }) + }), ); }); @@ -186,10 +186,10 @@ describe('public APIs', () => { expect(mockAuthenticatedHandler).toHaveBeenCalledWith( expect.objectContaining({ url: new URL( - 'https://123.execute-api.us-west-2.amazonaws.com/development/items/123' + 'https://123.execute-api.us-west-2.amazonaws.com/development/items/123', ), }), - expect.anything() + expect.anything(), ); }); @@ -209,7 +209,7 @@ describe('public APIs', () => { href: 'https://123.execute-api.us-west-2.amazonaws.com/development/items?param1=value1', }), }), - expect.anything() + expect.anything(), ); }); @@ -229,7 +229,7 @@ describe('public APIs', () => { href: 'https://123.execute-api.us-west-2.amazonaws.com/development/items?param1=value1&foo=bar', }), }), - expect.anything() + expect.anything(), ); }); @@ -243,7 +243,7 @@ describe('public APIs', () => { } catch (error) { expect(error).toBeInstanceOf(RestApiError); expect(error).toMatchObject( - validationErrorMap[RestApiValidationErrorCode.InvalidApiName] + validationErrorMap[RestApiValidationErrorCode.InvalidApiName], ); } }); @@ -260,7 +260,7 @@ describe('public APIs', () => { expect(error).toMatchObject({ ...validationErrorMap[RestApiValidationErrorCode.InvalidApiName], recoverySuggestion: expect.stringContaining( - 'Please make sure the REST endpoint URL is a valid URL string.' + 'Please make sure the REST endpoint URL is a valid URL string.', ), }); } @@ -277,7 +277,7 @@ describe('public APIs', () => { } catch (error) { expect(error).toBeInstanceOf(RestApiError); expect(error).toMatchObject( - validationErrorMap[RestApiValidationErrorCode.NoCredentials] + validationErrorMap[RestApiValidationErrorCode.NoCredentials], ); } }); @@ -294,7 +294,7 @@ describe('public APIs', () => { }, }; mockParseJsonError.mockResolvedValueOnce( - new RestApiError({ message: 'fooMessage', name: 'badRequest' }) + new RestApiError({ message: 'fooMessage', name: 'badRequest' }), ); mockAuthenticatedHandler.mockResolvedValueOnce(errorResponse); try { @@ -312,12 +312,12 @@ describe('public APIs', () => { it('should support cancel', async () => { expect.assertions(2); const abortSpy = jest.spyOn(AbortController.prototype, 'abort'); - let underLyingHandlerReject; + let underLyingHandlerReject: (reason?: any) => void; mockAuthenticatedHandler.mockReset(); mockAuthenticatedHandler.mockReturnValue( new Promise((_, reject) => { underLyingHandlerReject = reject; - }) + }), ); abortSpy.mockImplementation(() => { const mockAbortError = new Error('AbortError'); diff --git a/packages/api-rest/jest.config.js b/packages/api-rest/jest.config.js deleted file mode 100644 index f9b0f5880d9..00000000000 --- a/packages/api-rest/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 82, - functions: 89, - lines: 94, - statements: 95, - }, - }, -}; diff --git a/packages/api-rest/jest.config.mjs b/packages/api-rest/jest.config.mjs new file mode 100644 index 00000000000..c3e76b9d56b --- /dev/null +++ b/packages/api-rest/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 82, + functions: 89, + lines: 94, + statements: 95, + }, + }, +}); + +export default jestConfig; diff --git a/packages/api-rest/package.json b/packages/api-rest/package.json index 1e88a16e356..bcfad11f93d 100644 --- a/packages/api-rest/package.json +++ b/packages/api-rest/package.json @@ -11,19 +11,20 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:watch": "tslint 'src/**/*.ts' && jest -w 1 --watch", "build-with-test": "npm test && npm build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 70.0" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 70.0" }, "exports": { ".": { @@ -90,8 +91,7 @@ "@aws-amplify/core": "6.0.8", "@aws-amplify/react-native": "1.0.8", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/api-rest/setupTests.ts b/packages/api-rest/setupTests.ts index 980bec1759b..5309c7b030c 100644 --- a/packages/api-rest/setupTests.ts +++ b/packages/api-rest/setupTests.ts @@ -4,5 +4,4 @@ const anyGlobal = global as any; anyGlobal.navigator = anyGlobal.navigator || {}; -// @ts-ignore anyGlobal.navigator.sendBeacon = anyGlobal.navigator.sendBeacon || jest.fn(); diff --git a/packages/api-rest/src/apis/common/handler.ts b/packages/api-rest/src/apis/common/handler.ts index 2a7b555dad5..f1f0036c590 100644 --- a/packages/api-rest/src/apis/common/handler.ts +++ b/packages/api-rest/src/apis/common/handler.ts @@ -2,22 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; import { - HttpRequest, - unauthenticatedHandler, Headers, + HttpRequest, + authenticatedHandler, getRetryDecider, jitteredBackoff, - authenticatedHandler, + unauthenticatedHandler, } from '@aws-amplify/core/internals/aws-client-utils'; import { DocumentType } from '@aws-amplify/core/internals/utils'; - +import { RestApiResponse } from '~/src/types'; import { parseRestApiServiceError, parseSigningInfo, resolveCredentials, -} from '../../utils'; -import { resolveHeaders } from '../../utils/resolveHeaders'; -import { RestApiResponse } from '../../types'; +} from '~/src/utils'; +import { resolveHeaders } from '~/src/utils/resolveHeaders'; type HandlerOptions = Omit & { body?: DocumentType | FormData; @@ -25,10 +24,15 @@ type HandlerOptions = Omit & { withCredentials?: boolean; }; -type SigningServiceInfo = { +interface SigningServiceInfo { service?: string; region?: string; -}; +} + +const iamAuthApplicable = ( + { headers }: HttpRequest, + signingServiceInfo?: SigningServiceInfo, +) => !headers.authorization && !headers['x-api-key'] && !!signingServiceInfo; /** * Make REST API call with best-effort IAM auth. @@ -43,7 +47,7 @@ type SigningServiceInfo = { export const transferHandler = async ( amplify: AmplifyClassV6, options: HandlerOptions & { abortSignal: AbortSignal }, - signingServiceInfo?: SigningServiceInfo + signingServiceInfo?: SigningServiceInfo, ): Promise => { const { url, method, headers, body, withCredentials, abortSignal } = options; const resolvedBody = body @@ -85,6 +89,7 @@ export const transferHandler = async ( ...baseOptions, }); } + // Clean-up un-modeled properties from response. return { statusCode: response.statusCode, @@ -92,8 +97,3 @@ export const transferHandler = async ( body: response.body, }; }; - -const iamAuthApplicable = ( - { headers }: HttpRequest, - signingServiceInfo?: SigningServiceInfo -) => !headers.authorization && !headers['x-api-key'] && !!signingServiceInfo; diff --git a/packages/api-rest/src/apis/common/internalPost.ts b/packages/api-rest/src/apis/common/internalPost.ts index 187e54184a5..fadebe42700 100644 --- a/packages/api-rest/src/apis/common/internalPost.ts +++ b/packages/api-rest/src/apis/common/internalPost.ts @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; +import { InternalPostInput, RestApiResponse } from '~/src/types'; +import { createCancellableOperation } from '~/src/utils'; -import { InternalPostInput, RestApiResponse } from '../../types'; import { transferHandler } from './handler'; -import { createCancellableOperation } from '../../utils'; /** * This weak map provides functionality to cancel a request given the promise containing the `post` request. @@ -25,7 +25,7 @@ const cancelTokenMap = new WeakMap, AbortController>(); */ export const post = ( amplify: AmplifyClassV6, - { url, options, abortController }: InternalPostInput + { url, options, abortController }: InternalPostInput, ): Promise => { const controller = abortController ?? new AbortController(); const responsePromise = createCancellableOperation(async () => { @@ -37,14 +37,16 @@ export const post = ( ...options, abortSignal: controller.signal, }, - options?.signingServiceInfo + options?.signingServiceInfo, ); + return response; }, controller); const responseWithCleanUp = responsePromise.finally(() => { cancelTokenMap.delete(responseWithCleanUp); }); + return responseWithCleanUp; }; @@ -55,7 +57,7 @@ export const post = ( */ export const cancel = ( promise: Promise, - message?: string + message?: string, ): boolean => { const controller = cancelTokenMap.get(promise); if (controller) { @@ -63,10 +65,12 @@ export const cancel = ( if (message && controller.signal.reason !== message) { // In runtimes where `AbortSignal.reason` is not supported, we track the reason ourselves. // @ts-expect-error reason is read-only property. - controller.signal['reason'] = message; + controller.signal.reason = message; } + return true; } + return false; }; @@ -75,7 +79,7 @@ export const cancel = ( */ export const updateRequestToBeCancellable = ( promise: Promise, - controller: AbortController + controller: AbortController, ) => { cancelTokenMap.set(promise, controller); }; diff --git a/packages/api-rest/src/apis/common/publicApis.ts b/packages/api-rest/src/apis/common/publicApis.ts index 1034f77b811..72af51a31c9 100644 --- a/packages/api-rest/src/apis/common/publicApis.ts +++ b/packages/api-rest/src/apis/common/publicApis.ts @@ -3,33 +3,34 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; import { - GetInput, - GetOperation, - PostInput, - PostOperation, - PutInput, - PutOperation, + ApiInput, DeleteInput, DeleteOperation, + GetInput, + GetOperation, HeadInput, HeadOperation, PatchInput, PatchOperation, - ApiInput, + PostInput, + PostOperation, + PutInput, + PutOperation, RestApiOptionsBase, -} from '../../types'; +} from '~/src/types'; import { - resolveApiUrl, createCancellableOperation, logger, parseSigningInfo, -} from '../../utils'; + resolveApiUrl, +} from '~/src/utils'; + import { transferHandler } from './handler'; const publicHandler = ( amplify: AmplifyClassV6, options: ApiInput, - method: string + method: string, ) => createCancellableOperation(async abortSignal => { const { apiName, options: apiOptions = {}, path: apiPath } = options; @@ -37,7 +38,7 @@ const publicHandler = ( amplify, apiName, apiPath, - apiOptions?.queryParams + apiOptions?.queryParams, ); const libraryConfigHeaders = await amplify.libraryOptions?.API?.REST?.headers?.({ @@ -57,8 +58,9 @@ const publicHandler = ( method, url, headers, - `IAM signing options: ${JSON.stringify(signingServiceInfo)}` + `IAM signing options: ${JSON.stringify(signingServiceInfo)}`, ); + return transferHandler( amplify, { @@ -68,7 +70,7 @@ const publicHandler = ( headers, abortSignal, }, - signingServiceInfo + signingServiceInfo, ); }); @@ -77,7 +79,7 @@ export const get = (amplify: AmplifyClassV6, input: GetInput): GetOperation => export const post = ( amplify: AmplifyClassV6, - input: PostInput + input: PostInput, ): PostOperation => publicHandler(amplify, input, 'POST'); export const put = (amplify: AmplifyClassV6, input: PutInput): PutOperation => @@ -85,15 +87,15 @@ export const put = (amplify: AmplifyClassV6, input: PutInput): PutOperation => export const del = ( amplify: AmplifyClassV6, - input: DeleteInput + input: DeleteInput, ): DeleteOperation => publicHandler(amplify, input, 'DELETE'); export const head = ( amplify: AmplifyClassV6, - input: HeadInput + input: HeadInput, ): HeadOperation => publicHandler(amplify, input, 'HEAD'); export const patch = ( amplify: AmplifyClassV6, - input: PatchInput + input: PatchInput, ): PatchOperation => publicHandler(amplify, input, 'PATCH'); diff --git a/packages/api-rest/src/apis/index.ts b/packages/api-rest/src/apis/index.ts index a51f49d0afb..3e16cb9eb57 100644 --- a/packages/api-rest/src/apis/index.ts +++ b/packages/api-rest/src/apis/index.ts @@ -2,14 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { - get as commonGet, - post as commonPost, - put as commonPut, - del as commonDel, - head as commonHead, - patch as commonPatch, -} from './common/publicApis'; +import { RestApiError } from '~/src/errors'; import { DeleteInput, DeleteOperation, @@ -23,8 +16,16 @@ import { PostOperation, PutInput, PutOperation, -} from '../types'; -import { RestApiError } from '../errors'; +} from '~/src/types'; + +import { + del as commonDel, + get as commonGet, + head as commonHead, + patch as commonPatch, + post as commonPost, + put as commonPut, +} from './common/publicApis'; /** * GET HTTP request diff --git a/packages/api-rest/src/apis/server.ts b/packages/api-rest/src/apis/server.ts index 3afe1bb9e32..59de2a262a7 100644 --- a/packages/api-rest/src/apis/server.ts +++ b/packages/api-rest/src/apis/server.ts @@ -5,14 +5,7 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { - get as commonGet, - post as commonPost, - put as commonPut, - del as commonDel, - head as commonHead, - patch as commonPatch, -} from './common/publicApis'; +import { RestApiError } from '~/src/errors'; import { DeleteInput, DeleteOperation, @@ -26,8 +19,16 @@ import { PostOperation, PutInput, PutOperation, -} from '../types'; -import { RestApiError } from '../errors'; +} from '~/src/types'; + +import { + del as commonDel, + get as commonGet, + head as commonHead, + patch as commonPatch, + post as commonPost, + put as commonPut, +} from './common/publicApis'; /** * GET HTTP request (server-side) @@ -52,11 +53,10 @@ import { RestApiError } from '../errors'; * }, * }); * ``` - * @see {@link clientGet} */ export const get = ( contextSpec: AmplifyServer.ContextSpec, - input: GetInput + input: GetInput, ): GetOperation => commonGet(getAmplifyServerContext(contextSpec).amplify, input); @@ -86,7 +86,7 @@ export const get = ( */ export const post = ( contextSpec: AmplifyServer.ContextSpec, - input: PostInput + input: PostInput, ): PostOperation => commonPost(getAmplifyServerContext(contextSpec).amplify, input); @@ -116,7 +116,7 @@ export const post = ( */ export const put = ( contextSpec: AmplifyServer.ContextSpec, - input: PutInput + input: PutInput, ): PutOperation => commonPut(getAmplifyServerContext(contextSpec).amplify, input); @@ -145,7 +145,7 @@ export const put = ( */ export const del = ( contextSpec: AmplifyServer.ContextSpec, - input: DeleteInput + input: DeleteInput, ): DeleteOperation => commonDel(getAmplifyServerContext(contextSpec).amplify, input); @@ -174,7 +174,7 @@ export const del = ( */ export const head = ( contextSpec: AmplifyServer.ContextSpec, - input: HeadInput + input: HeadInput, ): HeadOperation => commonHead(getAmplifyServerContext(contextSpec).amplify, input); @@ -204,6 +204,6 @@ export const head = ( */ export const patch = ( contextSpec: AmplifyServer.ContextSpec, - input: PatchInput + input: PatchInput, ): PatchOperation => commonPatch(getAmplifyServerContext(contextSpec).amplify, input); diff --git a/packages/api-rest/src/errors/CanceledError.ts b/packages/api-rest/src/errors/CanceledError.ts index 254c2c69eef..ce4082212e0 100644 --- a/packages/api-rest/src/errors/CanceledError.ts +++ b/packages/api-rest/src/errors/CanceledError.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyErrorParams } from '@aws-amplify/core/internals/utils'; + import { RestApiError } from './RestApiError'; /** diff --git a/packages/api-rest/src/errors/assertValidatonError.ts b/packages/api-rest/src/errors/assertValidatonError.ts index e797fe1f16e..83ed1a353fd 100644 --- a/packages/api-rest/src/errors/assertValidatonError.ts +++ b/packages/api-rest/src/errors/assertValidatonError.ts @@ -9,7 +9,7 @@ import { RestApiValidationErrorCode, validationErrorMap } from './validation'; */ export function assertValidationError( assertion: boolean, - name: RestApiValidationErrorCode + name: RestApiValidationErrorCode, ): asserts assertion { const { message, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/api-rest/src/internals/index.ts b/packages/api-rest/src/internals/index.ts index 0076627c53c..3a9d0b5fc6c 100644 --- a/packages/api-rest/src/internals/index.ts +++ b/packages/api-rest/src/internals/index.ts @@ -1,9 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; - -import { post as internalPost } from '../apis/common/internalPost'; -import { InternalPostInput } from '../types'; +import { post as internalPost } from '~/src/apis/common/internalPost'; +import { InternalPostInput } from '~/src/types'; /** * Internal-only REST POST handler to send GraphQL request to given endpoint. By default, it will use IAM to authorize diff --git a/packages/api-rest/src/internals/server.ts b/packages/api-rest/src/internals/server.ts index 2cd55554e38..eeaed24469f 100644 --- a/packages/api-rest/src/internals/server.ts +++ b/packages/api-rest/src/internals/server.ts @@ -4,9 +4,8 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; - -import { post as internalPost } from '../apis/common/internalPost'; -import { InternalPostInput } from '../types'; +import { InternalPostInput } from '~/src/types'; +import { post as internalPost } from '~/src/apis/common/internalPost'; /** * Internal-only REST POST handler to send GraphQL request to given endpoint. By default, it will use IAM to authorize @@ -26,7 +25,7 @@ import { InternalPostInput } from '../types'; */ export const post = ( contextSpec: AmplifyServer.ContextSpec, - input: InternalPostInput + input: InternalPostInput, ) => { return internalPost(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/api-rest/src/types/index.ts b/packages/api-rest/src/types/index.ts index 5255586493d..f2f3c214b17 100644 --- a/packages/api-rest/src/types/index.ts +++ b/packages/api-rest/src/types/index.ts @@ -19,7 +19,7 @@ export type HeadOperation = Operation>; /** * @internal */ -export type RestApiOptionsBase = { +export interface RestApiOptionsBase { headers?: Headers; queryParams?: Record; body?: DocumentType | FormData; @@ -35,7 +35,7 @@ export type RestApiOptionsBase = { * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials} */ withCredentials?: boolean; -}; +} type Headers = Record; @@ -44,16 +44,16 @@ type Headers = Record; * * @internal */ -export type Operation = { +export interface Operation { response: Promise; - cancel: (abortMessage?: string) => void; -}; + cancel(abortMessage?: string): void; +} -type ResponsePayload = { - blob: () => Promise; - json: () => Promise; - text: () => Promise; -}; +interface ResponsePayload { + blob(): Promise; + json(): Promise; + text(): Promise; +} /** * Basic response type of REST API. @@ -69,7 +69,7 @@ export interface RestApiResponse { /** * @internal */ -export type ApiInput = { +export interface ApiInput { /** * Name of the REST API configured in Amplify singleton. */ @@ -82,13 +82,13 @@ export type ApiInput = { * Options to overwrite the REST API call behavior. */ options?: Options; -}; +} /** * Input type to invoke REST POST API from GraphQl client. * @internal */ -export type InternalPostInput = { +export interface InternalPostInput { // Resolved GraphQl endpoint url url: URL; options?: RestApiOptionsBase & { @@ -111,4 +111,4 @@ export type InternalPostInput = { * controller. */ abortController?: AbortController; -}; +} diff --git a/packages/api-rest/src/utils/createCancellableOperation.ts b/packages/api-rest/src/utils/createCancellableOperation.ts index 26535a7e71d..45258135379 100644 --- a/packages/api-rest/src/utils/createCancellableOperation.ts +++ b/packages/api-rest/src/utils/createCancellableOperation.ts @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { HttpResponse } from '@aws-amplify/core/internals/aws-client-utils'; -import { CanceledError } from '../errors'; -import { Operation } from '../types'; -import { parseRestApiServiceError } from './serviceError'; +import { CanceledError } from '~/src/errors'; +import { Operation } from '~/src/types'; + import { logger } from './logger'; +import { parseRestApiServiceError } from './serviceError'; /** * Create a cancellable operation conforming to the internal POST API interface. @@ -13,7 +14,7 @@ import { logger } from './logger'; */ export function createCancellableOperation( handler: () => Promise, - abortController: AbortController + abortController: AbortController, ): Promise; /** @@ -21,7 +22,7 @@ export function createCancellableOperation( * @internal */ export function createCancellableOperation( - handler: (signal: AbortSignal) => Promise + handler: (signal: AbortSignal) => Promise, ): Operation; /** @@ -31,13 +32,13 @@ export function createCancellableOperation( handler: | ((signal: AbortSignal) => Promise) | (() => Promise), - abortController?: AbortController + abortController?: AbortController, ): Operation | Promise { const isInternalPost = ( - handler: + handlerBeingChecked: | ((signal: AbortSignal) => Promise) - | (() => Promise) - ): handler is () => Promise => !!abortController; + | (() => Promise), + ): handlerBeingChecked is () => Promise => !!abortController; // For creating a cancellable operation for public REST APIs, we need to create an AbortController // internally. Whereas for internal POST APIs, we need to accept in the AbortController from the @@ -56,6 +57,7 @@ export function createCancellableOperation( if (response.statusCode >= 300) { throw await parseRestApiServiceError(response)!; } + return response; } catch (error: any) { const abortSignal = internalPostAbortSignal ?? publicApisAbortSignal; @@ -87,6 +89,7 @@ export function createCancellableOperation( abortReason = abortMessage; } }; + return { response: job(), cancel }; } } diff --git a/packages/api-rest/src/utils/parseSigningInfo.ts b/packages/api-rest/src/utils/parseSigningInfo.ts index 3414a6dd772..6c9e59df86e 100644 --- a/packages/api-rest/src/utils/parseSigningInfo.ts +++ b/packages/api-rest/src/utils/parseSigningInfo.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; + import { APIG_HOSTNAME_PATTERN, DEFAULT_IAM_SIGNING_REGION, @@ -19,7 +20,7 @@ export const parseSigningInfo = ( restApiOptions?: { amplify: AmplifyClassV6; apiName: string; - } + }, ) => { const { service: signingService = DEFAULT_REST_IAM_SIGNING_SERVICE, diff --git a/packages/api-rest/src/utils/resolveApiUrl.ts b/packages/api-rest/src/utils/resolveApiUrl.ts index f0cce97062a..6d104906906 100644 --- a/packages/api-rest/src/utils/resolveApiUrl.ts +++ b/packages/api-rest/src/utils/resolveApiUrl.ts @@ -11,7 +11,7 @@ import { RestApiValidationErrorCode, assertValidationError, validationErrorMap, -} from '../errors'; +} from '~/src/errors'; /** * Resolve the REST API request URL by: @@ -27,7 +27,7 @@ export const resolveApiUrl = ( amplify: AmplifyClassV6, apiName: string, path: string, - queryParams?: Record + queryParams?: Record, ): URL => { const urlStr = amplify.getConfig()?.API?.REST?.[apiName]?.endpoint; assertValidationError(!!urlStr, RestApiValidationErrorCode.InvalidApiName); @@ -40,6 +40,7 @@ export const resolveApiUrl = ( }); url.search = new AmplifyUrlSearchParams(mergedQueryParams).toString(); } + return url; } catch (error) { throw new RestApiError({ diff --git a/packages/api-rest/src/utils/resolveCredentials.ts b/packages/api-rest/src/utils/resolveCredentials.ts index 155606937aa..f91941fb5d1 100644 --- a/packages/api-rest/src/utils/resolveCredentials.ts +++ b/packages/api-rest/src/utils/resolveCredentials.ts @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; -import { RestApiValidationErrorCode, assertValidationError } from '../errors'; +import { + RestApiValidationErrorCode, + assertValidationError, +} from '~/src/errors'; /** * @internal @@ -11,7 +14,8 @@ export const resolveCredentials = async (amplify: AmplifyClassV6) => { const { credentials } = await amplify.Auth.fetchAuthSession(); assertValidationError( !!credentials && !!credentials.accessKeyId && !!credentials.secretAccessKey, - RestApiValidationErrorCode.NoCredentials + RestApiValidationErrorCode.NoCredentials, ); + return credentials; }; diff --git a/packages/api-rest/src/utils/resolveHeaders.ts b/packages/api-rest/src/utils/resolveHeaders.ts index fd6734ffa10..7950bcb0a67 100644 --- a/packages/api-rest/src/utils/resolveHeaders.ts +++ b/packages/api-rest/src/utils/resolveHeaders.ts @@ -3,10 +3,10 @@ export const resolveHeaders = ( headers?: Record, - body?: unknown + body?: unknown, ) => { const normalizedHeaders: Record = {}; - const isFormData = body instanceof FormData; + for (const key in headers) { normalizedHeaders[key.toLowerCase()] = headers[key]; } @@ -22,5 +22,6 @@ export const resolveHeaders = ( delete normalizedHeaders['content-type']; } } + return normalizedHeaders; }; diff --git a/packages/api-rest/src/utils/serviceError.ts b/packages/api-rest/src/utils/serviceError.ts index 1ab1fa40023..a44bf4b2637 100644 --- a/packages/api-rest/src/utils/serviceError.ts +++ b/packages/api-rest/src/utils/serviceError.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { MetadataBearer } from '@aws-sdk/types'; import { HttpResponse, parseJsonError, } from '@aws-amplify/core/internals/aws-client-utils'; -import { RestApiError } from '../errors'; +import { MetadataBearer } from '@aws-sdk/types'; +import { RestApiError } from '~/src/errors'; /** * Internal-only method to create a new RestApiError from a service error. @@ -18,17 +18,19 @@ export const buildRestApiServiceError = (error: Error): RestApiError => { message: error.message, underlyingError: error, }); + return restApiError; }; export const parseRestApiServiceError = async ( - response?: HttpResponse + response?: HttpResponse, ): Promise<(RestApiError & MetadataBearer) | undefined> => { const parsedError = await parseJsonError(response); if (!parsedError) { // Response is not an error. return; } + return Object.assign(buildRestApiServiceError(parsedError), { $metadata: parsedError.$metadata, }); diff --git a/packages/api-rest/tsconfig.build.json b/packages/api-rest/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/api-rest/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/api-rest/tsconfig.json b/packages/api-rest/tsconfig.json index f907c964821..cc4ae13d539 100644 --- a/packages/api-rest/tsconfig.json +++ b/packages/api-rest/tsconfig.json @@ -1,9 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/api-rest/tsconfig.test.json b/packages/api-rest/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/api-rest/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/api-rest/tsconfig.watch.json b/packages/api-rest/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/api-rest/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/api-rest/tslint.json b/packages/api-rest/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/api-rest/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/api/.eslintrc.js b/packages/api/.eslintrc.js new file mode 100644 index 00000000000..f977e58a3a1 --- /dev/null +++ b/packages/api/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/api', + }, + ], + }, +}; diff --git a/packages/api/jest.config.js b/packages/api/jest.config.js deleted file mode 100644 index 3a0e3340f9f..00000000000 --- a/packages/api/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100, - }, - }, -}; diff --git a/packages/api/jest.config.mjs b/packages/api/jest.config.mjs new file mode 100644 index 00000000000..7c9c9dd41cb --- /dev/null +++ b/packages/api/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); + +export default jestConfig; diff --git a/packages/api/package.json b/packages/api/package.json index b1c8c71d6fd..215bde517c5 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -14,18 +14,19 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 88" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 88" }, "exports": { ".": { @@ -69,8 +70,7 @@ "homepage": "https://aws-amplify.github.io/", "devDependencies": { "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "files": [ "dist/cjs", diff --git a/packages/api/src/API.ts b/packages/api/src/API.ts index 7a6d0dabbf6..db0559e4477 100644 --- a/packages/api/src/API.ts +++ b/packages/api/src/API.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { V6Client, CommonPublicClientOptions } from '@aws-amplify/api-graphql'; +import { CommonPublicClientOptions, V6Client } from '@aws-amplify/api-graphql'; import { generateClient as internalGenerateClient } from '@aws-amplify/api-graphql/internals'; import { Amplify } from '@aws-amplify/core'; @@ -8,7 +8,7 @@ import { Amplify } from '@aws-amplify/core'; * Generates an API client that can work with models or raw GraphQL */ export function generateClient = never>( - options: CommonPublicClientOptions = {} + options: CommonPublicClientOptions = {}, ): V6Client { return internalGenerateClient({ ...options, diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 158eb967a11..3181089331b 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -1,6 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import type { V6Client } from '@aws-amplify/api-graphql'; + export { GraphQLQuery, GraphQLSubscription, SelectionSet } from './types'; export { generateClient } from './API'; @@ -12,8 +14,6 @@ export type { CONNECTION_STATE_CHANGE, } from '@aws-amplify/api-graphql'; -import type { V6Client } from '@aws-amplify/api-graphql'; - // explicitly defaulting to `never` here resolves // TS2589: Type instantiation is excessively deep and possibly infinite. // When this type is used without a generic type arg, i.e. `let client: Client` diff --git a/packages/api/src/internals/InternalAPI.ts b/packages/api/src/internals/InternalAPI.ts index c8c7e6d8f50..c36d71eac6e 100644 --- a/packages/api/src/internals/InternalAPI.ts +++ b/packages/api/src/internals/InternalAPI.ts @@ -4,13 +4,13 @@ import { AWSAppSyncRealTimeProvider, GraphQLOperation, GraphQLOptions, - GraphQLResult, - OperationTypeNode, GraphQLQuery, + GraphQLResult, GraphQLSubscription, + OperationTypeNode, } from '@aws-amplify/api-graphql'; import { InternalGraphQLAPIClass } from '@aws-amplify/api-graphql/internals'; -import { Amplify, Cache, ConsoleLogger } from '@aws-amplify/core'; +import { Amplify, Cache } from '@aws-amplify/core'; import { ApiAction, Category, @@ -28,7 +28,6 @@ import { CustomHeaders } from '@aws-amplify/data-schema-types'; * state as possible for V6 to reduce number of potentially impactful changes to DataStore. */ -const logger = new ConsoleLogger('API'); /** * @deprecated * Use RestApi or GraphQLAPI to reduce your application bundle size @@ -68,7 +67,7 @@ export class InternalAPIClass { graphql( options: GraphQLOptions, additionalHeaders?: CustomHeaders, - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ): T extends GraphQLQuery ? Promise> : T extends GraphQLSubscription @@ -77,10 +76,11 @@ export class InternalAPIClass { value: GraphQLResult; }> : Promise> | Observable; - graphql( + + graphql( options: GraphQLOptions, additionalHeaders?: CustomHeaders, - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ): Promise> | Observable { const apiUserAgentDetails: CustomUserAgentDetails = { category: Category.API, @@ -92,7 +92,7 @@ export class InternalAPIClass { Amplify, options, additionalHeaders, - apiUserAgentDetails + apiUserAgentDetails, ); } } diff --git a/packages/api/tsconfig.build.json b/packages/api/tsconfig.build.json new file mode 100644 index 00000000000..b202c57cc23 --- /dev/null +++ b/packages/api/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__", "__mocks__"] +} diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index e477014534b..4e9f520d150 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -1,11 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "skipLibCheck": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + }, + "strictNullChecks": true }, - "include": ["./src"] + "include": ["./src", "__tests__", "__mocks__"] } diff --git a/packages/api/tsconfig.test.json b/packages/api/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/api/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/api/tsconfig.watch.json b/packages/api/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/api/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/api/tslint.json b/packages/api/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/api/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/auth/.eslintrc.js b/packages/auth/.eslintrc.js new file mode 100644 index 00000000000..24153c088d9 --- /dev/null +++ b/packages/auth/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/auth', + }, + ], + }, +}; diff --git a/packages/auth/jest.config.js b/packages/auth/jest.config.js deleted file mode 100644 index ce6816e489f..00000000000 --- a/packages/auth/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 64, - functions: 74, - lines: 83, - statements: 83, - }, - }, -}; diff --git a/packages/auth/jest.config.mjs b/packages/auth/jest.config.mjs new file mode 100644 index 00000000000..2957586192c --- /dev/null +++ b/packages/auth/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 64, + functions: 74, + lines: 83, + statements: 83, + }, + }, +}); + +export default jestConfig; diff --git a/packages/auth/package.json b/packages/auth/package.json index 6945605818d..65bde2b3046 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -14,18 +14,19 @@ "access": "public" }, "scripts": { - "test": "yarn lint --fix && jest -w 1 --coverage --logHeapUsage", + "test": "yarn lint --fix && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf lib-esm lib dist", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint '{src}/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 91.18" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 91.18" }, "typesVersions": { ">=4.2": { @@ -93,7 +94,6 @@ "@aws-amplify/react-native": "1.0.8", "@jest/test-sequencer": "^29.7.0", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" } } diff --git a/packages/auth/src/Errors.ts b/packages/auth/src/Errors.ts index 22b88d09db3..a64da33a0fb 100644 --- a/packages/auth/src/Errors.ts +++ b/packages/auth/src/Errors.ts @@ -3,8 +3,9 @@ // TODO: delete this module when the Auth class is removed. -import { AuthErrorMessages, AuthErrorTypes } from './types/Auth'; import { ConsoleLogger } from '@aws-amplify/core'; + +import { AuthErrorMessages, AuthErrorTypes } from './types/Auth'; import { AuthErrorStrings } from './common/AuthErrorStrings'; const logger = new ConsoleLogger('AuthError'); diff --git a/packages/auth/src/common/AuthErrorStrings.ts b/packages/auth/src/common/AuthErrorStrings.ts index 200ed2d6520..515afc2b070 100644 --- a/packages/auth/src/common/AuthErrorStrings.ts +++ b/packages/auth/src/common/AuthErrorStrings.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyErrorMap } from '@aws-amplify/core/internals/utils'; -import { AuthValidationErrorCode } from '../errors/types/validation'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; export const validationErrorMap: AmplifyErrorMap = { [AuthValidationErrorCode.EmptyChallengeResponse]: { diff --git a/packages/auth/src/errors/utils/assertServiceError.ts b/packages/auth/src/errors/utils/assertServiceError.ts index 8036b4cd755..4be89bf17cf 100644 --- a/packages/auth/src/errors/utils/assertServiceError.ts +++ b/packages/auth/src/errors/utils/assertServiceError.ts @@ -1,14 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthError } from '../AuthError'; +import { AuthError } from '~/src/errors/AuthError'; import { AmplifyErrorCode, ServiceError, } from '@aws-amplify/core/internals/utils'; export function assertServiceError( - error: unknown + error: unknown, ): asserts error is ServiceError { if ( !error || diff --git a/packages/auth/src/errors/utils/assertValidationError.ts b/packages/auth/src/errors/utils/assertValidationError.ts index 6ffb2d237df..391fc1f99e2 100644 --- a/packages/auth/src/errors/utils/assertValidationError.ts +++ b/packages/auth/src/errors/utils/assertValidationError.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { validationErrorMap } from '../../common/AuthErrorStrings'; -import { AuthError } from '../AuthError'; -import { AuthValidationErrorCode } from '../types/validation'; +import { validationErrorMap } from '~/src/common/AuthErrorStrings'; +import { AuthError } from '~/src/errors/AuthError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; export function assertValidationError( assertion: boolean, - name: AuthValidationErrorCode + name: AuthValidationErrorCode, ): asserts assertion { const { message, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/auth/src/providers/cognito/apis/autoSignIn.ts b/packages/auth/src/providers/cognito/apis/autoSignIn.ts index 7ee698b3b9c..f3599c7a58f 100644 --- a/packages/auth/src/providers/cognito/apis/autoSignIn.ts +++ b/packages/auth/src/providers/cognito/apis/autoSignIn.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthError } from '../../../errors/AuthError'; -import { AUTO_SIGN_IN_EXCEPTION } from '../../../errors/constants'; -import { AutoSignInCallback } from '../../../types/models'; -import { SignInOutput } from '../types'; +import { AuthError } from '~/src/errors/AuthError'; +import { AUTO_SIGN_IN_EXCEPTION } from '~/src/errors/constants'; +import { AutoSignInCallback } from '~/src/types/models'; +import { SignInOutput } from '~/src/providers/cognito/types'; const initialAutoSignIn: AutoSignInCallback = async (): Promise => { @@ -97,6 +97,8 @@ const initialAutoSignIn: AutoSignInCallback = * * ``` */ +// TODO(eslint): remove this linter suppression by refactoring. +// eslint-disable-next-line import/no-mutable-exports export let autoSignIn: AutoSignInCallback = initialAutoSignIn; /** diff --git a/packages/auth/src/providers/cognito/apis/confirmResetPassword.ts b/packages/auth/src/providers/cognito/apis/confirmResetPassword.ts index 1fcd4867d2f..29788d9d7f4 100644 --- a/packages/auth/src/providers/cognito/apis/confirmResetPassword.ts +++ b/packages/auth/src/providers/cognito/apis/confirmResetPassword.ts @@ -3,17 +3,17 @@ import { Amplify } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { ConfirmResetPasswordInput } from '../types'; -import { confirmForgotPassword } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { ConfirmForgotPasswordException } from '../../cognito/types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; -import { getUserContextData } from '../utils/userContextData'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { ConfirmResetPasswordInput } from '~/src/providers/cognito/types'; +import { confirmForgotPassword } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { ConfirmForgotPasswordException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; +import { getUserContextData } from '~/src/providers/cognito/utils/userContextData'; /** * Confirms the new password and verification code to reset the password. * @@ -25,7 +25,7 @@ import { getUserContextData } from '../utils/userContextData'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function confirmResetPassword( - input: ConfirmResetPasswordInput + input: ConfirmResetPasswordInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -33,17 +33,17 @@ export async function confirmResetPassword( const { username, newPassword } = input; assertValidationError( !!username, - AuthValidationErrorCode.EmptyConfirmResetPasswordUsername + AuthValidationErrorCode.EmptyConfirmResetPasswordUsername, ); assertValidationError( !!newPassword, - AuthValidationErrorCode.EmptyConfirmResetPasswordNewPassword + AuthValidationErrorCode.EmptyConfirmResetPasswordNewPassword, ); const code = input.confirmationCode; assertValidationError( !!code, - AuthValidationErrorCode.EmptyConfirmResetPasswordConfirmationCode + AuthValidationErrorCode.EmptyConfirmResetPasswordConfirmationCode, ); const metadata = input.options?.clientMetadata; @@ -64,7 +64,7 @@ export async function confirmResetPassword( Password: newPassword, ClientMetadata: metadata, ClientId: authConfig.userPoolClientId, - UserContextData: UserContextData, - } + UserContextData, + }, ); } diff --git a/packages/auth/src/providers/cognito/apis/confirmSignIn.ts b/packages/auth/src/providers/cognito/apis/confirmSignIn.ts index b319416b196..4e947803687 100644 --- a/packages/auth/src/providers/cognito/apis/confirmSignIn.ts +++ b/packages/auth/src/providers/cognito/apis/confirmSignIn.ts @@ -2,38 +2,42 @@ // SPDX-License-Identifier: Apache-2.0 import { - VerifySoftwareTokenException, - RespondToAuthChallengeException, AssociateSoftwareTokenException, -} from '../types/errors'; -import { ConfirmSignInInput, ConfirmSignInOutput } from '../types'; + RespondToAuthChallengeException, + VerifySoftwareTokenException, +} from '~/src/providers/cognito/types/errors'; +import { + ConfirmSignInInput, + ConfirmSignInOutput, +} from '~/src/providers/cognito/types'; import { cleanActiveSignInState, setActiveSignInState, signInStore, -} from '../utils/signInStore'; -import { AuthError } from '../../../errors/AuthError'; +} from '~/src/providers/cognito/utils/signInStore'; +import { AuthError } from '~/src/errors/AuthError'; import { getNewDeviceMetatada, getSignInResult, getSignInResultFromError, handleChallengeName, -} from '../utils/signInHelpers'; -import { assertServiceError } from '../../../errors/utils/assertServiceError'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { AuthErrorCodes } from '../../../common/AuthErrorStrings'; +} from '~/src/providers/cognito/utils/signInHelpers'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { AuthErrorCodes } from '~/src/common/AuthErrorStrings'; import { Amplify, Hub } from '@aws-amplify/core'; import { AMPLIFY_SYMBOL, assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { cacheCognitoTokens } from '../tokenProvider/cacheTokens'; +import { cacheCognitoTokens } from '~/src/providers/cognito/tokenProvider/cacheTokens'; import { ChallengeName, ChallengeParameters, -} from '../utils/clients/CognitoIdentityProvider/types'; -import { tokenOrchestrator } from '../tokenProvider'; +} from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; + import { getCurrentUser } from './getCurrentUser'; /** @@ -52,7 +56,7 @@ import { getCurrentUser } from './getCurrentUser'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function confirmSignIn( - input: ConfirmSignInInput + input: ConfirmSignInInput, ): Promise { const { challengeResponse, options } = input; const { username, challengeName, signInSession, signInDetails } = @@ -65,7 +69,7 @@ export async function confirmSignIn( assertValidationError( !!challengeResponse, - AuthValidationErrorCode.EmptyChallengeResponse + AuthValidationErrorCode.EmptyChallengeResponse, ); if (!username || !challengeName || !signInSession) @@ -88,9 +92,9 @@ export async function confirmSignIn( try { const { Session, - ChallengeName, + ChallengeName: handledChallengeName, AuthenticationResult, - ChallengeParameters, + ChallengeParameters: handledChallengeParameters, } = await handleChallengeName( username, challengeName as ChallengeName, @@ -99,14 +103,14 @@ export async function confirmSignIn( authConfig, tokenOrchestrator, clientMetaData, - options + options, ); // sets up local state used during the sign-in process setActiveSignInState({ signInSession: Session, username, - challengeName: ChallengeName as ChallengeName, + challengeName: handledChallengeName as ChallengeName, signInDetails, }); @@ -118,7 +122,7 @@ export async function confirmSignIn( NewDeviceMetadata: await getNewDeviceMetatada( authConfig.userPoolId, AuthenticationResult.NewDeviceMetadata, - AuthenticationResult.AccessToken + AuthenticationResult.AccessToken, ), signInDetails, }); @@ -129,8 +133,9 @@ export async function confirmSignIn( data: await getCurrentUser(), }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); + return { isSignedIn: true, nextStep: { signInStep: 'DONE' }, @@ -138,8 +143,8 @@ export async function confirmSignIn( } return getSignInResult({ - challengeName: ChallengeName as ChallengeName, - challengeParameters: ChallengeParameters as ChallengeParameters, + challengeName: handledChallengeName as ChallengeName, + challengeParameters: handledChallengeParameters as ChallengeParameters, }); } catch (error) { assertServiceError(error); diff --git a/packages/auth/src/providers/cognito/apis/confirmSignUp.ts b/packages/auth/src/providers/cognito/apis/confirmSignUp.ts index 3fe5f79209c..6ba9ca70ed9 100644 --- a/packages/auth/src/providers/cognito/apis/confirmSignUp.ts +++ b/packages/auth/src/providers/cognito/apis/confirmSignUp.ts @@ -3,24 +3,27 @@ import { Amplify } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, HubInternal, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { ConfirmSignUpInput, ConfirmSignUpOutput } from '../types'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { ConfirmSignUpException } from '../types/errors'; -import { confirmSignUp as confirmSignUpClient } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { AutoSignInEventData } from '../types/models'; +import { + ConfirmSignUpInput, + ConfirmSignUpOutput, +} from '~/src/providers/cognito/types'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { ConfirmSignUpException } from '~/src/providers/cognito/types/errors'; +import { confirmSignUp as confirmSignUpClient } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { AutoSignInEventData } from '~/src/providers/cognito/types/models'; import { isAutoSignInStarted, isAutoSignInUserUsingConfirmSignUp, setAutoSignInStarted, -} from '../utils/signUpHelpers'; -import { getAuthUserAgentValue } from '../../../utils'; -import { getUserContextData } from '../utils/userContextData'; +} from '~/src/providers/cognito/utils/signUpHelpers'; +import { getAuthUserAgentValue } from '~/src/utils'; +import { getUserContextData } from '~/src/providers/cognito/utils/userContextData'; /** * Confirms a new user account. @@ -34,7 +37,7 @@ import { getUserContextData } from '../utils/userContextData'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function confirmSignUp( - input: ConfirmSignUpInput + input: ConfirmSignUpInput, ): Promise { const { username, confirmationCode, options } = input; @@ -44,11 +47,11 @@ export async function confirmSignUp( const clientMetadata = options?.clientMetadata; assertValidationError( !!username, - AuthValidationErrorCode.EmptyConfirmSignUpUsername + AuthValidationErrorCode.EmptyConfirmSignUpUsername, ); assertValidationError( !!confirmationCode, - AuthValidationErrorCode.EmptyConfirmSignUpCode + AuthValidationErrorCode.EmptyConfirmSignUpCode, ); const UserContextData = getUserContextData({ @@ -69,7 +72,7 @@ export async function confirmSignUp( ForceAliasCreation: options?.forceAliasCreation, ClientId: authConfig.userPoolClientId, UserContextData, - } + }, ); return new Promise((resolve, reject) => { @@ -85,7 +88,9 @@ export async function confirmSignUp( !isAutoSignInStarted() || !isAutoSignInUserUsingConfirmSignUp(username) ) { - return resolve(signUpOut); + resolve(signUpOut); + + return; } const stopListener = HubInternal.listen( @@ -102,7 +107,7 @@ export async function confirmSignUp( setAutoSignInStarted(false); stopListener(); } - } + }, ); HubInternal.dispatch('auth-internal', { diff --git a/packages/auth/src/providers/cognito/apis/confirmUserAttribute.ts b/packages/auth/src/providers/cognito/apis/confirmUserAttribute.ts index 37dbc5888d4..6b0266bca17 100644 --- a/packages/auth/src/providers/cognito/apis/confirmUserAttribute.ts +++ b/packages/auth/src/providers/cognito/apis/confirmUserAttribute.ts @@ -3,17 +3,17 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { verifyUserAttribute } from '../utils/clients/CognitoIdentityProvider'; -import { VerifyUserAttributeException } from '../types/errors'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { ConfirmUserAttributeInput } from '../types'; -import { getAuthUserAgentValue } from '../../../utils'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { verifyUserAttribute } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { VerifyUserAttributeException } from '~/src/providers/cognito/types/errors'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { ConfirmUserAttributeInput } from '~/src/providers/cognito/types'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Confirms a user attribute with the confirmation code. @@ -25,14 +25,14 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function confirmUserAttribute( - input: ConfirmUserAttributeInput + input: ConfirmUserAttributeInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); const { confirmationCode, userAttributeKey } = input; assertValidationError( !!confirmationCode, - AuthValidationErrorCode.EmptyConfirmUserAttributeCode + AuthValidationErrorCode.EmptyConfirmUserAttributeCode, ); const { tokens } = await fetchAuthSession({ forceRefresh: false }); assertAuthTokens(tokens); @@ -45,6 +45,6 @@ export async function confirmUserAttribute( AccessToken: tokens.accessToken.toString(), AttributeName: userAttributeKey, Code: confirmationCode, - } + }, ); } diff --git a/packages/auth/src/providers/cognito/apis/deleteUser.ts b/packages/auth/src/providers/cognito/apis/deleteUser.ts index 1ef4ca242f6..f4e8ef998a9 100644 --- a/packages/auth/src/providers/cognito/apis/deleteUser.ts +++ b/packages/auth/src/providers/cognito/apis/deleteUser.ts @@ -3,16 +3,17 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { deleteUser as serviceDeleteUser } from '../utils/clients/CognitoIdentityProvider'; -import { DeleteUserException } from '../types/errors'; -import { tokenOrchestrator } from '../tokenProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { deleteUser as serviceDeleteUser } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { DeleteUserException } from '~/src/providers/cognito/types/errors'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; +import { getAuthUserAgentValue } from '~/src/utils'; + import { signOut } from './signOut'; -import { getAuthUserAgentValue } from '../../../utils'; /** * Deletes a user from the user pool while authenticated. @@ -34,7 +35,7 @@ export async function deleteUser(): Promise { }, { AccessToken: tokens.accessToken.toString(), - } + }, ); await tokenOrchestrator.clearDeviceMetadata(); await signOut(); diff --git a/packages/auth/src/providers/cognito/apis/deleteUserAttributes.ts b/packages/auth/src/providers/cognito/apis/deleteUserAttributes.ts index 89201a6ef52..21bb4c16c1e 100644 --- a/packages/auth/src/providers/cognito/apis/deleteUserAttributes.ts +++ b/packages/auth/src/providers/cognito/apis/deleteUserAttributes.ts @@ -3,15 +3,15 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { deleteUserAttributes as deleteUserAttributesClient } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { DeleteUserAttributesInput } from '../types'; -import { DeleteUserAttributesException } from '../types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; +import { deleteUserAttributes as deleteUserAttributesClient } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { DeleteUserAttributesInput } from '~/src/providers/cognito/types'; +import { DeleteUserAttributesException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Deletes user attributes. @@ -21,7 +21,7 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function deleteUserAttributes( - input: DeleteUserAttributesInput + input: DeleteUserAttributesInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -36,6 +36,6 @@ export async function deleteUserAttributes( { AccessToken: tokens.accessToken.toString(), UserAttributeNames: userAttributeKeys, - } + }, ); } diff --git a/packages/auth/src/providers/cognito/apis/fetchDevices.ts b/packages/auth/src/providers/cognito/apis/fetchDevices.ts index 3b697ea5c81..e36f8f90ec0 100644 --- a/packages/auth/src/providers/cognito/apis/fetchDevices.ts +++ b/packages/auth/src/providers/cognito/apis/fetchDevices.ts @@ -3,16 +3,16 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { FetchDevicesOutput } from '../types'; -import { listDevices } from '../utils/clients/CognitoIdentityProvider'; -import { DeviceType } from '../utils/clients/CognitoIdentityProvider/types'; -import { assertAuthTokens } from '../utils/types'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; +import { FetchDevicesOutput } from '~/src/providers/cognito/types'; +import { listDevices } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { DeviceType } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; import { rememberDevice } from '..'; -import { getAuthUserAgentValue } from '../../../utils'; +import { getAuthUserAgentValue } from '~/src/utils'; // Cognito Documentation for max device // https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListDevices.html#API_ListDevices_RequestSyntax @@ -41,13 +41,14 @@ export async function fetchDevices(): Promise { { AccessToken: tokens.accessToken.toString(), Limit: MAX_DEVICES, - } + }, ); + return parseDevicesResponse(response.Devices ?? []); } const parseDevicesResponse = async ( - devices: DeviceType[] + devices: DeviceType[], ): Promise => { return devices.map( ({ @@ -62,10 +63,12 @@ const parseDevicesResponse = async ( if (Name && Value) { attrs[Name] = Value; } + return attrs; }, - {} + {}, ); + return { id, attributes, @@ -79,6 +82,6 @@ const parseDevicesResponse = async ( ? new Date(DeviceLastAuthenticatedDate * 1000) : undefined, }; - } + }, ); }; diff --git a/packages/auth/src/providers/cognito/apis/fetchMFAPreference.ts b/packages/auth/src/providers/cognito/apis/fetchMFAPreference.ts index 44dc90415af..c06eed310e6 100644 --- a/packages/auth/src/providers/cognito/apis/fetchMFAPreference.ts +++ b/packages/auth/src/providers/cognito/apis/fetchMFAPreference.ts @@ -1,18 +1,21 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { FetchMFAPreferenceOutput } from '../types'; -import { getMFAType, getMFATypes } from '../utils/signInHelpers'; -import { GetUserException } from '../types/errors'; -import { getUser } from '../utils/clients/CognitoIdentityProvider'; +import { FetchMFAPreferenceOutput } from '~/src/providers/cognito/types'; +import { + getMFAType, + getMFATypes, +} from '~/src/providers/cognito/utils/signInHelpers'; +import { GetUserException } from '~/src/providers/cognito/types/errors'; +import { getUser } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { getAuthUserAgentValue } from '../../../utils'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Fetches the preferred MFA setting and enabled MFA settings for the user. @@ -34,7 +37,7 @@ export async function fetchMFAPreference(): Promise { }, { AccessToken: tokens.accessToken.toString(), - } + }, ); return { diff --git a/packages/auth/src/providers/cognito/apis/fetchUserAttributes.ts b/packages/auth/src/providers/cognito/apis/fetchUserAttributes.ts index 79ba1e48b8a..eaed9668ed4 100644 --- a/packages/auth/src/providers/cognito/apis/fetchUserAttributes.ts +++ b/packages/auth/src/providers/cognito/apis/fetchUserAttributes.ts @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { FetchUserAttributesOutput } from '../types'; +import { FetchUserAttributesOutput } from '~/src/providers/cognito/types'; + import { fetchUserAttributes as fetchUserAttributesInternal } from './internal/fetchUserAttributes'; /** diff --git a/packages/auth/src/providers/cognito/apis/forgetDevice.ts b/packages/auth/src/providers/cognito/apis/forgetDevice.ts index 8ea78399fef..2cb34d63fee 100644 --- a/packages/auth/src/providers/cognito/apis/forgetDevice.ts +++ b/packages/auth/src/providers/cognito/apis/forgetDevice.ts @@ -1,18 +1,21 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { forgetDevice as serviceForgetDevice } from '../utils/clients/CognitoIdentityProvider'; +import { forgetDevice as serviceForgetDevice } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; import { Amplify, fetchAuthSession } from '@aws-amplify/core'; -import { assertAuthTokens, assertDeviceMetadata } from '../utils/types'; import { - assertTokenProviderConfig, + assertAuthTokens, + assertDeviceMetadata, +} from '~/src/providers/cognito/utils/types'; +import { AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { tokenOrchestrator } from '../tokenProvider'; -import { ForgetDeviceInput } from '../types'; -import { ForgetDeviceException } from '../../cognito/types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; +import { ForgetDeviceInput } from '~/src/providers/cognito/types'; +import { ForgetDeviceException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Forget a remembered device while authenticated. @@ -42,7 +45,7 @@ export async function forgetDevice(input?: ForgetDeviceInput): Promise { { AccessToken: tokens.accessToken.toString(), DeviceKey: externalDeviceKey ?? currentDeviceKey, - } + }, ); if (!externalDeviceKey || externalDeviceKey === currentDeviceKey) diff --git a/packages/auth/src/providers/cognito/apis/getCurrentUser.ts b/packages/auth/src/providers/cognito/apis/getCurrentUser.ts index db5a9c30235..5fc307e1fa6 100644 --- a/packages/auth/src/providers/cognito/apis/getCurrentUser.ts +++ b/packages/auth/src/providers/cognito/apis/getCurrentUser.ts @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { GetCurrentUserOutput } from '../types'; +import { GetCurrentUserOutput } from '~/src/providers/cognito/types'; + import { getCurrentUser as getCurrentUserInternal } from './internal/getCurrentUser'; /** diff --git a/packages/auth/src/providers/cognito/apis/internal/fetchUserAttributes.ts b/packages/auth/src/providers/cognito/apis/internal/fetchUserAttributes.ts index 718e8ca6ca0..73f0a087640 100644 --- a/packages/auth/src/providers/cognito/apis/internal/fetchUserAttributes.ts +++ b/packages/auth/src/providers/cognito/apis/internal/fetchUserAttributes.ts @@ -7,15 +7,15 @@ import { assertTokenProviderConfig, fetchAuthSession, } from '@aws-amplify/core/internals/utils'; -import { getUser } from '../../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../../utils/types'; -import { FetchUserAttributesOutput } from '../../types'; -import { toAuthUserAttribute } from '../../utils/apiHelpers'; -import { getAuthUserAgentValue } from '../../../../utils'; +import { getUser } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { FetchUserAttributesOutput } from '~/src/providers/cognito/types'; +import { toAuthUserAttribute } from '~/src/providers/cognito/utils/apiHelpers'; +import { getAuthUserAgentValue } from '~/src/utils'; export const fetchUserAttributes = async ( - amplify: AmplifyClassV6 + amplify: AmplifyClassV6, ): Promise => { const authConfig = amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -31,7 +31,7 @@ export const fetchUserAttributes = async ( }, { AccessToken: tokens.accessToken.toString(), - } + }, ); return toAuthUserAttribute(UserAttributes); diff --git a/packages/auth/src/providers/cognito/apis/internal/getCurrentUser.ts b/packages/auth/src/providers/cognito/apis/internal/getCurrentUser.ts index 395512d66a4..76772fe10df 100644 --- a/packages/auth/src/providers/cognito/apis/internal/getCurrentUser.ts +++ b/packages/auth/src/providers/cognito/apis/internal/getCurrentUser.ts @@ -3,15 +3,15 @@ import { AmplifyClassV6, AuthTokens } from '@aws-amplify/core'; import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils'; -import { assertAuthTokens } from '../../utils/types'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; import { - CognitoAuthSignInDetails, AuthUser, + CognitoAuthSignInDetails, GetCurrentUserOutput, -} from '../../types'; +} from '~/src/providers/cognito/types'; export const getCurrentUser = async ( - amplify: AmplifyClassV6 + amplify: AmplifyClassV6, ): Promise => { const authConfig = amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -29,11 +29,12 @@ export const getCurrentUser = async ( if (signInDetails) { authUser.signInDetails = signInDetails; } + return authUser; }; function getSignInDetailsFromTokens( - tokens: AuthTokens & { signInDetails?: CognitoAuthSignInDetails } + tokens: AuthTokens & { signInDetails?: CognitoAuthSignInDetails }, ): CognitoAuthSignInDetails | undefined { return tokens?.signInDetails; } diff --git a/packages/auth/src/providers/cognito/apis/rememberDevice.ts b/packages/auth/src/providers/cognito/apis/rememberDevice.ts index 0623998cd08..2f182e525db 100644 --- a/packages/auth/src/providers/cognito/apis/rememberDevice.ts +++ b/packages/auth/src/providers/cognito/apis/rememberDevice.ts @@ -1,17 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { updateDeviceStatus } from '../utils/clients/CognitoIdentityProvider'; +import { updateDeviceStatus } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; import { Amplify, fetchAuthSession } from '@aws-amplify/core'; -import { assertAuthTokens, assertDeviceMetadata } from '../utils/types'; import { - assertTokenProviderConfig, + assertAuthTokens, + assertDeviceMetadata, +} from '~/src/providers/cognito/utils/types'; +import { AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { tokenOrchestrator } from '../tokenProvider'; -import { UpdateDeviceStatusException } from '../../cognito/types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; +import { UpdateDeviceStatusException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Marks device as remembered while authenticated. @@ -39,6 +42,6 @@ export async function rememberDevice(): Promise { AccessToken: tokens.accessToken.toString(), DeviceKey: deviceMetadata.deviceKey, DeviceRememberedStatus: 'remembered', - } + }, ); } diff --git a/packages/auth/src/providers/cognito/apis/resendSignUpCode.ts b/packages/auth/src/providers/cognito/apis/resendSignUpCode.ts index 53446d600d2..edd22312e3b 100644 --- a/packages/auth/src/providers/cognito/apis/resendSignUpCode.ts +++ b/packages/auth/src/providers/cognito/apis/resendSignUpCode.ts @@ -3,18 +3,21 @@ import { Amplify } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, AuthVerifiableAttributeKey, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthDeliveryMedium } from '../../../types'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { ResendSignUpCodeInput, ResendSignUpCodeOutput } from '../types'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { resendConfirmationCode } from '../utils/clients/CognitoIdentityProvider'; -import { getAuthUserAgentValue } from '../../../utils'; -import { getUserContextData } from '../utils/userContextData'; +import { AuthDeliveryMedium } from '~/src/types'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { + ResendSignUpCodeInput, + ResendSignUpCodeOutput, +} from '~/src/providers/cognito/types'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { resendConfirmationCode } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getAuthUserAgentValue } from '~/src/utils'; +import { getUserContextData } from '~/src/providers/cognito/utils/userContextData'; /** * Resend the confirmation code while signing up @@ -26,12 +29,12 @@ import { getUserContextData } from '../utils/userContextData'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function resendSignUpCode( - input: ResendSignUpCodeInput + input: ResendSignUpCodeInput, ): Promise { - const username = input.username; + const { username } = input; assertValidationError( !!username, - AuthValidationErrorCode.EmptySignUpUsername + AuthValidationErrorCode.EmptySignUpUsername, ); const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -54,11 +57,12 @@ export async function resendSignUpCode( ClientMetadata: clientMetadata, ClientId: authConfig.userPoolClientId, UserContextData, - } + }, ); const { DeliveryMedium, AttributeName, Destination } = { ...CodeDeliveryDetails, }; + return { destination: Destination as string, deliveryMedium: DeliveryMedium as AuthDeliveryMedium, diff --git a/packages/auth/src/providers/cognito/apis/resetPassword.ts b/packages/auth/src/providers/cognito/apis/resetPassword.ts index 17455ada185..a1b31557dcc 100644 --- a/packages/auth/src/providers/cognito/apis/resetPassword.ts +++ b/packages/auth/src/providers/cognito/apis/resetPassword.ts @@ -3,19 +3,22 @@ import { Amplify } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, AuthVerifiableAttributeKey, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { AuthDeliveryMedium } from '../../../types'; -import { ResetPasswordInput, ResetPasswordOutput } from '../types'; -import { forgotPassword } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { ForgotPasswordException } from '../../cognito/types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; -import { getUserContextData } from '../utils/userContextData'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { AuthDeliveryMedium } from '~/src/types'; +import { + ResetPasswordInput, + ResetPasswordOutput, +} from '~/src/providers/cognito/types'; +import { forgotPassword } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { ForgotPasswordException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; +import { getUserContextData } from '~/src/providers/cognito/utils/userContextData'; /** * Resets a user's password. @@ -29,12 +32,12 @@ import { getUserContextData } from '../utils/userContextData'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. **/ export async function resetPassword( - input: ResetPasswordInput + input: ResetPasswordInput, ): Promise { - const username = input.username; + const { username } = input; assertValidationError( !!username, - AuthValidationErrorCode.EmptyResetPasswordUsername + AuthValidationErrorCode.EmptyResetPasswordUsername, ); const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -57,9 +60,10 @@ export async function resetPassword( ClientMetadata: clientMetadata, ClientId: authConfig.userPoolClientId, UserContextData, - } + }, ); const codeDeliveryDetails = res.CodeDeliveryDetails; + return { isPasswordReset: false, nextStep: { diff --git a/packages/auth/src/providers/cognito/apis/sendUserAttributeVerificationCode.ts b/packages/auth/src/providers/cognito/apis/sendUserAttributeVerificationCode.ts index d3e7562073a..a3a445cbf71 100644 --- a/packages/auth/src/providers/cognito/apis/sendUserAttributeVerificationCode.ts +++ b/packages/auth/src/providers/cognito/apis/sendUserAttributeVerificationCode.ts @@ -3,20 +3,20 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, AuthVerifiableAttributeKey, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthDeliveryMedium } from '../../../types'; +import { AuthDeliveryMedium } from '~/src/types'; import { SendUserAttributeVerificationCodeInput, SendUserAttributeVerificationCodeOutput, -} from '../types'; -import { getUserAttributeVerificationCode } from '../utils/clients/CognitoIdentityProvider'; -import { assertAuthTokens } from '../utils/types'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { GetUserAttributeVerificationException } from '../types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; +} from '~/src/providers/cognito/types'; +import { getUserAttributeVerificationCode } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { GetUserAttributeVerificationException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Resends user's confirmation code when updating attributes while authenticated. @@ -27,7 +27,7 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export const sendUserAttributeVerificationCode = async ( - input: SendUserAttributeVerificationCodeInput + input: SendUserAttributeVerificationCodeInput, ): Promise => { const { userAttributeKey, options } = input; const authConfig = Amplify.getConfig().Auth?.Cognito; @@ -39,18 +39,19 @@ export const sendUserAttributeVerificationCode = async ( { region: getRegion(authConfig.userPoolId), userAgentValue: getAuthUserAgentValue( - AuthAction.SendUserAttributeVerificationCode + AuthAction.SendUserAttributeVerificationCode, ), }, { AccessToken: tokens.accessToken.toString(), ClientMetadata: clientMetadata, AttributeName: userAttributeKey, - } + }, ); const { DeliveryMedium, AttributeName, Destination } = { ...CodeDeliveryDetails, }; + return { destination: Destination, deliveryMedium: DeliveryMedium as AuthDeliveryMedium, diff --git a/packages/auth/src/providers/cognito/apis/server/fetchUserAttributes.ts b/packages/auth/src/providers/cognito/apis/server/fetchUserAttributes.ts index b80f5ea4a2f..bac5ade769c 100644 --- a/packages/auth/src/providers/cognito/apis/server/fetchUserAttributes.ts +++ b/packages/auth/src/providers/cognito/apis/server/fetchUserAttributes.ts @@ -5,13 +5,13 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { FetchUserAttributesOutput } from '../../types'; -import { fetchUserAttributes as fetchUserAttributesInternal } from '../internal/fetchUserAttributes'; +import { FetchUserAttributesOutput } from '~/src/providers/cognito/types'; +import { fetchUserAttributes as fetchUserAttributesInternal } from '~/src/providers/cognito/apis/internal/fetchUserAttributes'; export const fetchUserAttributes = ( - contextSpec: AmplifyServer.ContextSpec + contextSpec: AmplifyServer.ContextSpec, ): Promise => { return fetchUserAttributesInternal( - getAmplifyServerContext(contextSpec).amplify + getAmplifyServerContext(contextSpec).amplify, ); }; diff --git a/packages/auth/src/providers/cognito/apis/server/getCurrentUser.ts b/packages/auth/src/providers/cognito/apis/server/getCurrentUser.ts index daa7d7081eb..6232cdadb9b 100644 --- a/packages/auth/src/providers/cognito/apis/server/getCurrentUser.ts +++ b/packages/auth/src/providers/cognito/apis/server/getCurrentUser.ts @@ -5,8 +5,8 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { GetCurrentUserOutput } from '../../types'; -import { getCurrentUser as getCurrentUserInternal } from '../internal/getCurrentUser'; +import { GetCurrentUserOutput } from '~/src/providers/cognito/types'; +import { getCurrentUser as getCurrentUserInternal } from '~/src/providers/cognito/apis/internal/getCurrentUser'; /** * Gets the current user from the idToken. @@ -16,7 +16,7 @@ import { getCurrentUser as getCurrentUserInternal } from '../internal/getCurrent * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export const getCurrentUser = async ( - contextSpec: AmplifyServer.ContextSpec + contextSpec: AmplifyServer.ContextSpec, ): Promise => { return getCurrentUserInternal(getAmplifyServerContext(contextSpec).amplify); }; diff --git a/packages/auth/src/providers/cognito/apis/setUpTOTP.ts b/packages/auth/src/providers/cognito/apis/setUpTOTP.ts index e3e30f72ae3..940a5ffe184 100644 --- a/packages/auth/src/providers/cognito/apis/setUpTOTP.ts +++ b/packages/auth/src/providers/cognito/apis/setUpTOTP.ts @@ -3,20 +3,20 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthError } from '../../../errors/AuthError'; +import { AuthError } from '~/src/errors/AuthError'; import { - SETUP_TOTP_EXCEPTION, AssociateSoftwareTokenException, -} from '../types/errors'; -import { SetUpTOTPOutput } from '../types'; -import { getTOTPSetupDetails } from '../utils/signInHelpers'; -import { associateSoftwareToken } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { getAuthUserAgentValue } from '../../../utils'; + SETUP_TOTP_EXCEPTION, +} from '~/src/providers/cognito/types/errors'; +import { SetUpTOTPOutput } from '~/src/providers/cognito/types'; +import { getTOTPSetupDetails } from '~/src/providers/cognito/utils/signInHelpers'; +import { associateSoftwareToken } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Sets up TOTP for the user. @@ -39,7 +39,7 @@ export async function setUpTOTP(): Promise { }, { AccessToken: tokens.accessToken.toString(), - } + }, ); if (!SecretCode) { @@ -49,5 +49,6 @@ export async function setUpTOTP(): Promise { message: 'Failed to set up TOTP.', }); } + return getTOTPSetupDetails(SecretCode, JSON.stringify(username)); } diff --git a/packages/auth/src/providers/cognito/apis/signIn.ts b/packages/auth/src/providers/cognito/apis/signIn.ts index 40a3cc98b49..84de077e7ab 100644 --- a/packages/auth/src/providers/cognito/apis/signIn.ts +++ b/packages/auth/src/providers/cognito/apis/signIn.ts @@ -4,14 +4,15 @@ import { InitiateAuthException, RespondToAuthChallengeException, -} from '../types/errors'; +} from '~/src/providers/cognito/types/errors'; +import { assertUserNotAuthenticated } from '~/src/providers/cognito/utils/signInHelpers'; +import { SignInInput, SignInOutput } from '~/src/providers/cognito/types'; + import { signInWithCustomAuth } from './signInWithCustomAuth'; import { signInWithCustomSRPAuth } from './signInWithCustomSRPAuth'; import { signInWithSRP } from './signInWithSRP'; import { signInWithUserPassword } from './signInWithUserPassword'; -import { assertUserNotAuthenticated } from '../utils/signInHelpers'; -import { SignInInput, SignInOutput } from '../types'; /** * Signs a user in * diff --git a/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts b/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts index ce9a1be44b4..ccc4d032b35 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithCustomAuth.ts @@ -1,38 +1,39 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { assertServiceError } from '../../../errors/utils/assertServiceError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; import { - handleCustomAuthFlowWithoutSRP, + getActiveSignInUsername, + getNewDeviceMetatada, getSignInResult, getSignInResultFromError, - getNewDeviceMetatada, + handleCustomAuthFlowWithoutSRP, retryOnResourceNotFoundException, - getActiveSignInUsername, -} from '../utils/signInHelpers'; +} from '~/src/providers/cognito/utils/signInHelpers'; import { Amplify, Hub } from '@aws-amplify/core'; import { AMPLIFY_SYMBOL, assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { InitiateAuthException } from '../types/errors'; +import { InitiateAuthException } from '~/src/providers/cognito/types/errors'; import { CognitoAuthSignInDetails, SignInWithCustomAuthInput, SignInWithCustomAuthOutput, -} from '../types'; +} from '~/src/providers/cognito/types'; import { cleanActiveSignInState, setActiveSignInState, -} from '../utils/signInStore'; -import { cacheCognitoTokens } from '../tokenProvider/cacheTokens'; +} from '~/src/providers/cognito/utils/signInStore'; +import { cacheCognitoTokens } from '~/src/providers/cognito/tokenProvider/cacheTokens'; import { ChallengeName, ChallengeParameters, -} from '../utils/clients/CognitoIdentityProvider/types'; -import { tokenOrchestrator } from '../tokenProvider'; +} from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; + import { getCurrentUser } from './getCurrentUser'; /** @@ -46,7 +47,7 @@ import { getCurrentUser } from './getCurrentUser'; * @throws SignInWithCustomAuthOutput - Thrown when the token provider config is invalid. */ export async function signInWithCustomAuth( - input: SignInWithCustomAuthInput + input: SignInWithCustomAuthInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -58,31 +59,31 @@ export async function signInWithCustomAuth( const metadata = options?.clientMetadata; assertValidationError( !!username, - AuthValidationErrorCode.EmptySignInUsername + AuthValidationErrorCode.EmptySignInUsername, ); assertValidationError( !password, - AuthValidationErrorCode.CustomAuthSignInPassword + AuthValidationErrorCode.CustomAuthSignInPassword, ); try { const { - ChallengeName, - ChallengeParameters, + ChallengeName: retryChallengeName, + ChallengeParameters: retryChallengeParameters, AuthenticationResult, Session, } = await retryOnResourceNotFoundException( handleCustomAuthFlowWithoutSRP, [username, metadata, authConfig, tokenOrchestrator], username, - tokenOrchestrator + tokenOrchestrator, ); const activeUsername = getActiveSignInUsername(username); // sets up local state used during the sign-in process setActiveSignInState({ signInSession: Session, username: activeUsername, - challengeName: ChallengeName as ChallengeName, + challengeName: retryChallengeName as ChallengeName, signInDetails, }); if (AuthenticationResult) { @@ -94,7 +95,7 @@ export async function signInWithCustomAuth( NewDeviceMetadata: await getNewDeviceMetatada( authConfig.userPoolId, AuthenticationResult.NewDeviceMetadata, - AuthenticationResult.AccessToken + AuthenticationResult.AccessToken, ), signInDetails, }); @@ -102,8 +103,9 @@ export async function signInWithCustomAuth( 'auth', { event: 'signedIn', data: await getCurrentUser() }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); + return { isSignedIn: true, nextStep: { signInStep: 'DONE' }, @@ -111,8 +113,8 @@ export async function signInWithCustomAuth( } return getSignInResult({ - challengeName: ChallengeName as ChallengeName, - challengeParameters: ChallengeParameters as ChallengeParameters, + challengeName: retryChallengeName as ChallengeName, + challengeParameters: retryChallengeParameters as ChallengeParameters, }); } catch (error) { cleanActiveSignInState(); diff --git a/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts b/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts index c092bf692fa..8b568a5626f 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithCustomSRPAuth.ts @@ -6,35 +6,36 @@ import { AMPLIFY_SYMBOL, assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { assertServiceError } from '../../../errors/utils/assertServiceError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; import { - handleCustomSRPAuthFlow, + getActiveSignInUsername, + getNewDeviceMetatada, getSignInResult, getSignInResultFromError, - getNewDeviceMetatada, - getActiveSignInUsername, -} from '../utils/signInHelpers'; + handleCustomSRPAuthFlow, +} from '~/src/providers/cognito/utils/signInHelpers'; import { InitiateAuthException, RespondToAuthChallengeException, -} from '../types/errors'; +} from '~/src/providers/cognito/types/errors'; import { CognitoAuthSignInDetails, SignInWithCustomSRPAuthInput, SignInWithCustomSRPAuthOutput, -} from '../types'; +} from '~/src/providers/cognito/types'; import { cleanActiveSignInState, setActiveSignInState, -} from '../utils/signInStore'; -import { cacheCognitoTokens } from '../tokenProvider/cacheTokens'; +} from '~/src/providers/cognito/utils/signInStore'; +import { cacheCognitoTokens } from '~/src/providers/cognito/tokenProvider/cacheTokens'; import { ChallengeName, ChallengeParameters, -} from '../utils/clients/CognitoIdentityProvider/types'; -import { tokenOrchestrator } from '../tokenProvider'; +} from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; + import { getCurrentUser } from './getCurrentUser'; /** @@ -49,7 +50,7 @@ import { getCurrentUser } from './getCurrentUser'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function signInWithCustomSRPAuth( - input: SignInWithCustomSRPAuthInput + input: SignInWithCustomSRPAuthInput, ): Promise { const { username, password, options } = input; const signInDetails: CognitoAuthSignInDetails = { @@ -61,17 +62,17 @@ export async function signInWithCustomSRPAuth( const metadata = options?.clientMetadata; assertValidationError( !!username, - AuthValidationErrorCode.EmptySignInUsername + AuthValidationErrorCode.EmptySignInUsername, ); assertValidationError( !!password, - AuthValidationErrorCode.EmptySignInPassword + AuthValidationErrorCode.EmptySignInPassword, ); try { const { - ChallengeName, - ChallengeParameters, + ChallengeName: handledChallengeName, + ChallengeParameters: handledChallengeParameters, AuthenticationResult, Session, } = await handleCustomSRPAuthFlow( @@ -79,7 +80,7 @@ export async function signInWithCustomSRPAuth( password, metadata, authConfig, - tokenOrchestrator + tokenOrchestrator, ); const activeUsername = getActiveSignInUsername(username); @@ -87,7 +88,7 @@ export async function signInWithCustomSRPAuth( setActiveSignInState({ signInSession: Session, username: activeUsername, - challengeName: ChallengeName as ChallengeName, + challengeName: handledChallengeName as ChallengeName, signInDetails, }); if (AuthenticationResult) { @@ -97,7 +98,7 @@ export async function signInWithCustomSRPAuth( NewDeviceMetadata: await getNewDeviceMetatada( authConfig.userPoolId, AuthenticationResult.NewDeviceMetadata, - AuthenticationResult.AccessToken + AuthenticationResult.AccessToken, ), signInDetails, }); @@ -109,8 +110,9 @@ export async function signInWithCustomSRPAuth( data: await getCurrentUser(), }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); + return { isSignedIn: true, nextStep: { signInStep: 'DONE' }, @@ -118,8 +120,8 @@ export async function signInWithCustomSRPAuth( } return getSignInResult({ - challengeName: ChallengeName as ChallengeName, - challengeParameters: ChallengeParameters as ChallengeParameters, + challengeName: handledChallengeName as ChallengeName, + challengeParameters: handledChallengeParameters as ChallengeParameters, }); } catch (error) { cleanActiveSignInState(); diff --git a/packages/auth/src/providers/cognito/apis/signInWithRedirect.ts b/packages/auth/src/providers/cognito/apis/signInWithRedirect.ts index 13756284ec6..4f688f5d931 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithRedirect.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithRedirect.ts @@ -1,33 +1,37 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Amplify, Hub, defaultStorage, OAuthConfig } from '@aws-amplify/core'; +import { Amplify, Hub, OAuthConfig, defaultStorage } from '@aws-amplify/core'; import { - AuthAction, AMPLIFY_SYMBOL, + AmplifyUrl, + AuthAction, + USER_AGENT_HEADER, assertOAuthConfig, assertTokenProviderConfig, + decodeJWT, isBrowser, - urlSafeEncode, - USER_AGENT_HEADER, urlSafeDecode, - decodeJWT, - AmplifyUrl, + urlSafeEncode, } from '@aws-amplify/core/internals/utils'; -import { cacheCognitoTokens } from '../tokenProvider/cacheTokens'; -import { cognitoUserPoolsTokenProvider } from '../tokenProvider'; -import { cognitoHostedUIIdentityProviderMap } from '../types/models'; -import { DefaultOAuthStore } from '../utils/signInWithRedirectStore'; -import { AuthError } from '../../../errors/AuthError'; -import { AuthErrorTypes } from '../../../types/Auth'; -import { AuthErrorCodes } from '../../../common/AuthErrorStrings'; -import { authErrorMessages } from '../../../Errors'; -import { getAuthUserAgentValue, openAuthSession } from '../../../utils'; -import { assertUserNotAuthenticated } from '../utils/signInHelpers'; -import { SignInWithRedirectInput } from '../types'; -import { generateCodeVerifier, generateState } from '../utils/oauth'; +import { cacheCognitoTokens } from '~/src/providers/cognito/tokenProvider/cacheTokens'; +import { cognitoUserPoolsTokenProvider } from '~/src/providers/cognito/tokenProvider'; +import { cognitoHostedUIIdentityProviderMap } from '~/src/providers/cognito/types/models'; +import { DefaultOAuthStore } from '~/src/providers/cognito/utils/signInWithRedirectStore'; +import { AuthError } from '~/src/errors/AuthError'; +import { AuthErrorTypes } from '~/src/types/Auth'; +import { AuthErrorCodes } from '~/src/common/AuthErrorStrings'; +import { authErrorMessages } from '~/src/Errors'; +import { getAuthUserAgentValue, openAuthSession } from '~/src/utils'; +import { assertUserNotAuthenticated } from '~/src/providers/cognito/utils/signInHelpers'; +import { SignInWithRedirectInput } from '~/src/providers/cognito/types'; +import { + generateCodeVerifier, + generateState, +} from '~/src/providers/cognito/utils/oauth'; +import { getRedirectUrl } from '~/src/providers/cognito/utils/oauth/getRedirectUrl'; + import { getCurrentUser } from './getCurrentUser'; -import { getRedirectUrl } from '../utils/oauth/getRedirectUrl'; /** * Signs in a user with OAuth. Redirects the application to an Identity Provider. @@ -38,7 +42,7 @@ import { getRedirectUrl } from '../utils/oauth/getRedirectUrl'; * @throws OAuthNotConfigureException - Thrown when the oauth config is invalid. */ export async function signInWithRedirect( - input?: SignInWithRedirectInput + input?: SignInWithRedirectInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); @@ -81,11 +85,11 @@ export async function oauthSignIn({ const { domain, redirectSignIn, responseType, scopes } = oauthConfig; const randomState = generateState(); - /* encodeURIComponent is not URL safe, use urlSafeEncode instead. Cognito + /* encodeURIComponent is not URL safe, use urlSafeEncode instead. Cognito single-encodes/decodes url on first sign in and double-encodes/decodes url - when user already signed in. Using encodeURIComponent, Base32, Base64 add - characters % or = which on further encoding becomes unsafe. '=' create issue - for parsing query params. + when user already signed in. Using encodeURIComponent, Base32, Base64 add + characters % or = which on further encoding becomes unsafe. '=' create issue + for parsing query params. Refer: https://github.com/aws-amplify/amplify-js/issues/5218 */ const state = customState ? `${randomState}-${urlSafeEncode(customState)}` @@ -93,13 +97,13 @@ export async function oauthSignIn({ const { value, method, toCodeChallenge } = generateCodeVerifier(128); const redirectUri = getRedirectUrl(oauthConfig.redirectSignIn); - + store.storeOAuthInFlight(true); store.storeOAuthState(state); store.storePKCE(value); const queryString = Object.entries({ - redirect_uri: redirectUri , + redirect_uri: redirectUri, response_type: responseType, client_id: clientId, identity_provider: provider, @@ -165,8 +169,9 @@ async function handleCodeFlow({ invokeAndClearPromise(); // validateState method will always throw an AuthError when the state is not valid. The if statement is making TS happy. if (err instanceof AuthError) { - await handleFailure(err.message); + await handleFailure((err as AuthError).message); } + return; } const code = url.searchParams.get('code'); @@ -174,6 +179,7 @@ async function handleCodeFlow({ if (!code) { await store.clearOAuthData(); invokeAndClearPromise(); + return; } @@ -201,13 +207,13 @@ async function handleCodeFlow({ .join('&'); const { - access_token, - refresh_token, - id_token, + access_token: accessToken, + refresh_token: refreshToken, + id_token: idToken, error, - error_message, - token_type, - expires_in, + error_message: errorMessage, + token_type: tokenType, + expires_in: expiresIn, } = await ( await fetch(oAuthTokenEndpoint, { method: 'POST', @@ -221,21 +227,21 @@ async function handleCodeFlow({ if (error) { invokeAndClearPromise(); - await handleFailure(error_message ?? error); + await handleFailure(errorMessage ?? error); } await store.clearOAuthInflightData(); const username = - (access_token && decodeJWT(access_token).payload.username) ?? 'username'; + (accessToken && decodeJWT(accessToken).payload.username) ?? 'username'; await cacheCognitoTokens({ username, - AccessToken: access_token, - IdToken: id_token, - RefreshToken: refresh_token, - TokenType: token_type, - ExpiresIn: expires_in, + AccessToken: accessToken, + IdToken: idToken, + RefreshToken: refreshToken, + TokenType: tokenType, + ExpiresIn: expiresIn, }); return completeFlow({ @@ -288,17 +294,19 @@ async function handleImplicitFlow({ if (!access_token) { await store.clearOAuthData(); invokeAndClearPromise(); + return; } let validatedState; try { validatedState = await validateState(state); - } catch (error) { + } catch (err) { invokeAndClearPromise(); // validateState method will always throw an AuthError when the state is not valid. The if statement is making TS happy. - if (error instanceof AuthError) { - await handleFailure(error.message); + if (err instanceof AuthError) { + await handleFailure((err as AuthError).message); } + return; } @@ -339,7 +347,7 @@ async function completeFlow({ data: urlSafeDecode(getCustomState(state)), }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); } Hub.dispatch('auth', { event: 'signInWithRedirect' }, 'Auth', AMPLIFY_SYMBOL); @@ -347,7 +355,7 @@ async function completeFlow({ 'auth', { event: 'signedIn', data: await getCurrentUser() }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); clearHistory(redirectUri); invokeAndClearPromise(); @@ -370,33 +378,29 @@ async function handleAuthResponse({ domain: string; preferPrivateSession?: boolean; }) { - try { - const urlParams = new AmplifyUrl(currentUrl); - const error = urlParams.searchParams.get('error'); - const errorMessage = urlParams.searchParams.get('error_description'); + const urlParams = new AmplifyUrl(currentUrl); + const error = urlParams.searchParams.get('error'); + const errorMessage = urlParams.searchParams.get('error_description'); - if (error) { - await handleFailure(errorMessage); - } + if (error) { + await handleFailure(errorMessage); + } - if (responseType === 'code') { - return await handleCodeFlow({ - currentUrl, - userAgentValue, - clientId, - redirectUri, - domain, - preferPrivateSession, - }); - } else { - return await handleImplicitFlow({ - currentUrl, - redirectUri, - preferPrivateSession, - }); - } - } catch (e) { - throw e; + if (responseType === 'code') { + await handleCodeFlow({ + currentUrl, + userAgentValue, + clientId, + redirectUri, + domain, + preferPrivateSession, + }); + } else { + await handleImplicitFlow({ + currentUrl, + redirectUri, + preferPrivateSession, + }); } } @@ -416,6 +420,7 @@ async function validateState(state?: string | null): Promise { recoverySuggestion: 'Try to initiate an OAuth flow from Amplify', }); } + return validatedState; } @@ -430,7 +435,7 @@ async function handleFailure(errorMessage: string | null) { 'auth', { event: 'signInWithRedirect_failure', data: { error } }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); throw new AuthError({ message: errorMessage ?? '', @@ -504,14 +509,15 @@ const invokeAndClearPromise = () => { isBrowser() && cognitoUserPoolsTokenProvider.setWaitForInflightOAuth( () => - new Promise(async (res, _rej) => { + // TODO(eslint): remove this linter suppression. + // eslint-disable-next-line no-async-promise-executor + new Promise(async resolve => { if (!(await store.loadOAuthInFlight())) { - res(); + resolve(); } else { - inflightPromiseResolvers.push(res); + inflightPromiseResolvers.push(resolve); } - return; - }) + }), ); function clearHistory(redirectUri: string) { @@ -520,7 +526,7 @@ function clearHistory(redirectUri: string) { } } -function isCustomState(state: string): Boolean { +function isCustomState(state: string): boolean { return /-/.test(state); } diff --git a/packages/auth/src/providers/cognito/apis/signInWithSRP.ts b/packages/auth/src/providers/cognito/apis/signInWithSRP.ts index 446fb841e32..f1e3570af50 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithSRP.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithSRP.ts @@ -1,17 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { assertServiceError } from '../../../errors/utils/assertServiceError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; import { ChallengeName, ChallengeParameters, -} from '../utils/clients/CognitoIdentityProvider/types'; +} from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; import { InitiateAuthException, RespondToAuthChallengeException, -} from '../types/errors'; +} from '~/src/providers/cognito/types/errors'; import { Amplify, Hub } from '@aws-amplify/core'; import { AMPLIFY_SYMBOL, @@ -23,18 +23,19 @@ import { getSignInResult, getSignInResultFromError, handleUserSRPAuthFlow, -} from '../utils/signInHelpers'; +} from '~/src/providers/cognito/utils/signInHelpers'; import { CognitoAuthSignInDetails, SignInWithSRPInput, SignInWithSRPOutput, -} from '../types'; +} from '~/src/providers/cognito/types'; import { - setActiveSignInState, cleanActiveSignInState, -} from '../utils/signInStore'; -import { cacheCognitoTokens } from '../tokenProvider/cacheTokens'; -import { tokenOrchestrator } from '../tokenProvider'; + setActiveSignInState, +} from '~/src/providers/cognito/utils/signInStore'; +import { cacheCognitoTokens } from '~/src/providers/cognito/tokenProvider/cacheTokens'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; + import { getCurrentUser } from './getCurrentUser'; /** @@ -49,7 +50,7 @@ import { getCurrentUser } from './getCurrentUser'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function signInWithSRP( - input: SignInWithSRPInput + input: SignInWithSRPInput, ): Promise { const { username, password } = input; const authConfig = Amplify.getConfig().Auth?.Cognito; @@ -61,17 +62,17 @@ export async function signInWithSRP( const clientMetaData = input.options?.clientMetadata; assertValidationError( !!username, - AuthValidationErrorCode.EmptySignInUsername + AuthValidationErrorCode.EmptySignInUsername, ); assertValidationError( !!password, - AuthValidationErrorCode.EmptySignInPassword + AuthValidationErrorCode.EmptySignInPassword, ); try { const { - ChallengeName, - ChallengeParameters, + ChallengeName: handledChallengeName, + ChallengeParameters: handledChallengeParameters, AuthenticationResult, Session, } = await handleUserSRPAuthFlow( @@ -79,7 +80,7 @@ export async function signInWithSRP( password, clientMetaData, authConfig, - tokenOrchestrator + tokenOrchestrator, ); const activeUsername = getActiveSignInUsername(username); @@ -87,7 +88,7 @@ export async function signInWithSRP( setActiveSignInState({ signInSession: Session, username: activeUsername, - challengeName: ChallengeName as ChallengeName, + challengeName: handledChallengeName as ChallengeName, signInDetails, }); if (AuthenticationResult) { @@ -98,7 +99,7 @@ export async function signInWithSRP( NewDeviceMetadata: await getNewDeviceMetatada( authConfig.userPoolId, AuthenticationResult.NewDeviceMetadata, - AuthenticationResult.AccessToken + AuthenticationResult.AccessToken, ), signInDetails, }); @@ -109,8 +110,9 @@ export async function signInWithSRP( data: await getCurrentUser(), }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); + return { isSignedIn: true, nextStep: { signInStep: 'DONE' }, @@ -118,8 +120,8 @@ export async function signInWithSRP( } return getSignInResult({ - challengeName: ChallengeName as ChallengeName, - challengeParameters: ChallengeParameters as ChallengeParameters, + challengeName: handledChallengeName as ChallengeName, + challengeParameters: handledChallengeParameters as ChallengeParameters, }); } catch (error) { cleanActiveSignInState(); diff --git a/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts b/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts index 414b15d9d9e..97d8a00c380 100644 --- a/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts +++ b/packages/auth/src/providers/cognito/apis/signInWithUserPassword.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertServiceError } from '../../../errors/utils/assertServiceError'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; import { ChallengeName, ChallengeParameters, -} from '../utils/clients/CognitoIdentityProvider/types'; +} from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; import { getActiveSignInUsername, getNewDeviceMetatada, @@ -15,24 +15,25 @@ import { getSignInResultFromError, handleUserPasswordAuthFlow, retryOnResourceNotFoundException, -} from '../utils/signInHelpers'; +} from '~/src/providers/cognito/utils/signInHelpers'; import { Amplify, Hub } from '@aws-amplify/core'; import { AMPLIFY_SYMBOL, assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { InitiateAuthException } from '../types/errors'; +import { InitiateAuthException } from '~/src/providers/cognito/types/errors'; import { CognitoAuthSignInDetails, SignInWithUserPasswordInput, SignInWithUserPasswordOutput, -} from '../types'; +} from '~/src/providers/cognito/types'; import { cleanActiveSignInState, setActiveSignInState, -} from '../utils/signInStore'; -import { cacheCognitoTokens } from '../tokenProvider/cacheTokens'; -import { tokenOrchestrator } from '../tokenProvider'; +} from '~/src/providers/cognito/utils/signInStore'; +import { cacheCognitoTokens } from '~/src/providers/cognito/tokenProvider/cacheTokens'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; + import { getCurrentUser } from './getCurrentUser'; /** @@ -46,7 +47,7 @@ import { getCurrentUser } from './getCurrentUser'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function signInWithUserPassword( - input: SignInWithUserPasswordInput + input: SignInWithUserPasswordInput, ): Promise { const { username, password, options } = input; const authConfig = Amplify.getConfig().Auth?.Cognito; @@ -58,31 +59,31 @@ export async function signInWithUserPassword( const metadata = options?.clientMetadata; assertValidationError( !!username, - AuthValidationErrorCode.EmptySignInUsername + AuthValidationErrorCode.EmptySignInUsername, ); assertValidationError( !!password, - AuthValidationErrorCode.EmptySignInPassword + AuthValidationErrorCode.EmptySignInPassword, ); try { const { - ChallengeName, - ChallengeParameters, + ChallengeName: retryChallengeName, + ChallengeParameters: retryChallengeParameters, AuthenticationResult, Session, } = await retryOnResourceNotFoundException( handleUserPasswordAuthFlow, [username, password, metadata, authConfig, tokenOrchestrator], username, - tokenOrchestrator + tokenOrchestrator, ); const activeUsername = getActiveSignInUsername(username); // sets up local state used during the sign-in process setActiveSignInState({ signInSession: Session, username: activeUsername, - challengeName: ChallengeName as ChallengeName, + challengeName: retryChallengeName as ChallengeName, signInDetails, }); if (AuthenticationResult) { @@ -92,7 +93,7 @@ export async function signInWithUserPassword( NewDeviceMetadata: await getNewDeviceMetatada( authConfig.userPoolId, AuthenticationResult.NewDeviceMetadata, - AuthenticationResult.AccessToken + AuthenticationResult.AccessToken, ), signInDetails, }); @@ -104,8 +105,9 @@ export async function signInWithUserPassword( data: await getCurrentUser(), }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); + return { isSignedIn: true, nextStep: { signInStep: 'DONE' }, @@ -113,8 +115,8 @@ export async function signInWithUserPassword( } return getSignInResult({ - challengeName: ChallengeName as ChallengeName, - challengeParameters: ChallengeParameters as ChallengeParameters, + challengeName: retryChallengeName as ChallengeName, + challengeParameters: retryChallengeParameters as ChallengeParameters, }); } catch (error) { cleanActiveSignInState(); diff --git a/packages/auth/src/providers/cognito/apis/signOut.ts b/packages/auth/src/providers/cognito/apis/signOut.ts index 6453d073202..96151a529d8 100644 --- a/packages/auth/src/providers/cognito/apis/signOut.ts +++ b/packages/auth/src/providers/cognito/apis/signOut.ts @@ -3,36 +3,35 @@ import { Amplify, - clearCredentials, CognitoUserPoolConfig, ConsoleLogger, - defaultStorage, Hub, + clearCredentials, + defaultStorage, } from '@aws-amplify/core'; - -import { getAuthUserAgentValue } from '../../../utils'; -import { SignOutInput } from '../types'; -import { tokenOrchestrator } from '../tokenProvider'; +import { getAuthUserAgentValue } from '~/src/utils'; +import { SignOutInput } from '~/src/providers/cognito/types'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; import { - AuthAction, AMPLIFY_SYMBOL, + AuthAction, + JWT, assertOAuthConfig, assertTokenProviderConfig, - JWT, } from '@aws-amplify/core/internals/utils'; import { globalSignOut as globalSignOutClient, revokeToken, -} from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; +} from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; import { assertAuthTokens, assertAuthTokensWithRefreshToken, -} from '../utils/types'; -import { handleOAuthSignOut } from '../utils/oauth'; -import { DefaultOAuthStore } from '../utils/signInWithRedirectStore'; -import { AuthError } from '../../../errors/AuthError'; -import { OAUTH_SIGNOUT_EXCEPTION } from '../../../errors/constants'; +} from '~/src/providers/cognito/utils/types'; +import { handleOAuthSignOut } from '~/src/providers/cognito/utils/oauth'; +import { DefaultOAuthStore } from '~/src/providers/cognito/utils/signInWithRedirectStore'; +import { AuthError } from '~/src/errors/AuthError'; +import { OAUTH_SIGNOUT_EXCEPTION } from '~/src/errors/constants'; const logger = new ConsoleLogger('Auth'); @@ -94,13 +93,13 @@ async function clientSignOut(cognitoConfig: CognitoUserPoolConfig) { { ClientId: cognitoConfig.userPoolClientId, Token: authTokens.refreshToken, - } + }, ); } } catch (err) { // this shouldn't throw logger.debug( - 'Client signOut error caught but will proceed with token removal' + 'Client signOut error caught but will proceed with token removal', ); } } @@ -116,12 +115,12 @@ async function globalSignOut(cognitoConfig: CognitoUserPoolConfig) { }, { AccessToken: authTokens.accessToken.toString(), - } + }, ); } catch (err) { // it should not throw logger.debug( - 'Global signOut error caught but will proceed with token removal' + 'Global signOut error caught but will proceed with token removal', ); } } diff --git a/packages/auth/src/providers/cognito/apis/signUp.ts b/packages/auth/src/providers/cognito/apis/signUp.ts index b5bb844c4ce..3c8bedf611d 100644 --- a/packages/auth/src/providers/cognito/apis/signUp.ts +++ b/packages/auth/src/providers/cognito/apis/signUp.ts @@ -3,29 +3,34 @@ import { Amplify } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, AuthVerifiableAttributeKey, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthDeliveryMedium } from '../../../types'; -import { SignUpInput, SignUpOutput, SignInInput } from '../types'; -import { signUp as signUpClient } from '../utils/clients/CognitoIdentityProvider'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { SignUpException } from '../types/errors'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { toAttributeType } from '../utils/apiHelpers'; +import { AuthDeliveryMedium } from '~/src/types'; import { + SignInInput, + SignUpInput, + SignUpOutput, +} from '~/src/providers/cognito/types'; +import { signUp as signUpClient } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { SignUpException } from '~/src/providers/cognito/types/errors'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { toAttributeType } from '~/src/providers/cognito/utils/apiHelpers'; +import { + autoSignInUserConfirmed, + autoSignInWhenUserIsConfirmedWithLink, handleCodeAutoSignIn, isAutoSignInStarted, - setAutoSignInStarted, isSignUpComplete, - autoSignInUserConfirmed, - autoSignInWhenUserIsConfirmedWithLink, + setAutoSignInStarted, setUsernameUsedForAutoSignIn, -} from '../utils/signUpHelpers'; +} from '~/src/providers/cognito/utils/signUpHelpers'; +import { getAuthUserAgentValue } from '~/src/utils'; + import { setAutoSignIn } from './autoSignIn'; -import { getAuthUserAgentValue } from '../../../utils'; /** * Creates a user @@ -46,11 +51,11 @@ export async function signUp(input: SignUpInput): Promise { assertTokenProviderConfig(authConfig); assertValidationError( !!username, - AuthValidationErrorCode.EmptySignUpUsername + AuthValidationErrorCode.EmptySignUpUsername, ); assertValidationError( !!password, - AuthValidationErrorCode.EmptySignUpPassword + AuthValidationErrorCode.EmptySignUpPassword, ); const signInServiceOptions = @@ -63,7 +68,7 @@ export async function signUp(input: SignUpInput): Promise { // if the authFlowType is 'CUSTOM_WITHOUT_SRP' then we don't include the password if (signInServiceOptions?.authFlowType !== 'CUSTOM_WITHOUT_SRP') { - signInInput['password'] = password; + signInInput.password = password; } if (signInServiceOptions || autoSignIn === true) { setUsernameUsedForAutoSignIn(username); @@ -82,12 +87,13 @@ export async function signUp(input: SignUpInput): Promise { ClientMetadata: clientMetadata, ValidationData: validationData && toAttributeType(validationData), ClientId: authConfig.userPoolClientId, - } + }, ); const { UserSub, CodeDeliveryDetails } = clientOutput; if (isSignUpComplete(clientOutput) && isAutoSignInStarted()) { setAutoSignIn(autoSignInUserConfirmed(signInInput)); + return { isSignUpComplete: true, nextStep: { @@ -113,6 +119,7 @@ export async function signUp(input: SignUpInput): Promise { signUpVerificationMethod === 'link' ) { setAutoSignIn(autoSignInWhenUserIsConfirmedWithLink(signInInput)); + return { isSignUpComplete: false, nextStep: { diff --git a/packages/auth/src/providers/cognito/apis/updateMFAPreference.ts b/packages/auth/src/providers/cognito/apis/updateMFAPreference.ts index 116a384231c..921c8dfb96e 100644 --- a/packages/auth/src/providers/cognito/apis/updateMFAPreference.ts +++ b/packages/auth/src/providers/cognito/apis/updateMFAPreference.ts @@ -3,17 +3,17 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { UpdateMFAPreferenceInput } from '../types'; -import { SetUserMFAPreferenceException } from '../types/errors'; -import { MFAPreference } from '../types/models'; -import { setUserMFAPreference } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { CognitoMFASettings } from '../utils/clients/CognitoIdentityProvider/types'; -import { assertAuthTokens } from '../utils/types'; -import { getAuthUserAgentValue } from '../../../utils'; +import { UpdateMFAPreferenceInput } from '~/src/providers/cognito/types'; +import { SetUserMFAPreferenceException } from '~/src/providers/cognito/types/errors'; +import { MFAPreference } from '~/src/providers/cognito/types/models'; +import { setUserMFAPreference } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { CognitoMFASettings } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Updates the MFA preference of the user. @@ -23,7 +23,7 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function updateMFAPreference( - input: UpdateMFAPreferenceInput + input: UpdateMFAPreferenceInput, ): Promise { const { sms, totp } = input; const authConfig = Amplify.getConfig().Auth?.Cognito; @@ -39,12 +39,12 @@ export async function updateMFAPreference( AccessToken: tokens.accessToken.toString(), SMSMfaSettings: getMFASettings(sms), SoftwareTokenMfaSettings: getMFASettings(totp), - } + }, ); } export function getMFASettings( - mfaPreference?: MFAPreference + mfaPreference?: MFAPreference, ): CognitoMFASettings | undefined { if (mfaPreference === 'DISABLED') { return { diff --git a/packages/auth/src/providers/cognito/apis/updatePassword.ts b/packages/auth/src/providers/cognito/apis/updatePassword.ts index 955f4cae51a..28640b730b4 100644 --- a/packages/auth/src/providers/cognito/apis/updatePassword.ts +++ b/packages/auth/src/providers/cognito/apis/updatePassword.ts @@ -1,19 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { UpdatePasswordInput } from '../types'; -import { changePassword } from '../utils/clients/CognitoIdentityProvider'; -import { ChangePasswordException } from '../../cognito/types/errors'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { UpdatePasswordInput } from '~/src/providers/cognito/types'; +import { changePassword } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { ChangePasswordException } from '~/src/providers/cognito/types/errors'; import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { getAuthUserAgentValue } from '../../../utils'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Updates user's password while authenticated. @@ -24,19 +24,19 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function updatePassword( - input: UpdatePasswordInput + input: UpdatePasswordInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); const { oldPassword, newPassword } = input; assertValidationError( !!oldPassword, - AuthValidationErrorCode.EmptyUpdatePassword + AuthValidationErrorCode.EmptyUpdatePassword, ); assertValidationError( !!newPassword, - AuthValidationErrorCode.EmptyUpdatePassword + AuthValidationErrorCode.EmptyUpdatePassword, ); const { tokens } = await fetchAuthSession({ forceRefresh: false }); assertAuthTokens(tokens); @@ -49,6 +49,6 @@ export async function updatePassword( AccessToken: tokens.accessToken.toString(), PreviousPassword: oldPassword, ProposedPassword: newPassword, - } + }, ); } diff --git a/packages/auth/src/providers/cognito/apis/updateUserAttribute.ts b/packages/auth/src/providers/cognito/apis/updateUserAttribute.ts index e8a2af55f78..a841eca0829 100644 --- a/packages/auth/src/providers/cognito/apis/updateUserAttribute.ts +++ b/packages/auth/src/providers/cognito/apis/updateUserAttribute.ts @@ -1,8 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { UpdateUserAttributeInput, UpdateUserAttributeOutput } from '../types'; -import { UpdateUserAttributesException } from '../types/errors'; +import { + UpdateUserAttributeInput, + UpdateUserAttributeOutput, +} from '~/src/providers/cognito/types'; +import { UpdateUserAttributesException } from '~/src/providers/cognito/types/errors'; + import { updateUserAttributes } from './updateUserAttributes'; /** @@ -14,7 +18,7 @@ import { updateUserAttributes } from './updateUserAttributes'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export const updateUserAttribute = async ( - input: UpdateUserAttributeInput + input: UpdateUserAttributeInput, ): Promise => { const { userAttribute: { attributeKey, value }, @@ -24,5 +28,6 @@ export const updateUserAttribute = async ( userAttributes: { [attributeKey]: value }, options, }); + return Object.values(output)[0]; }; diff --git a/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts b/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts index f52e68005ed..32e9f446769 100644 --- a/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts +++ b/packages/auth/src/providers/cognito/apis/updateUserAttributes.ts @@ -3,25 +3,25 @@ import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; import { - AuthUserAttributes, - AuthUpdateUserAttributesOutput, AuthDeliveryMedium, -} from '../../../types'; + AuthUpdateUserAttributesOutput, + AuthUserAttributes, +} from '~/src/types'; import { UpdateUserAttributesInput, UpdateUserAttributesOutput, -} from '../types'; -import { updateUserAttributes as updateUserAttributesClient } from '../utils/clients/CognitoIdentityProvider'; -import { assertAuthTokens } from '../utils/types'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { toAttributeType } from '../utils/apiHelpers'; -import { CodeDeliveryDetailsType } from '../utils/clients/CognitoIdentityProvider/types'; -import { UpdateUserAttributesException } from '../types/errors'; -import { getAuthUserAgentValue } from '../../../utils'; +} from '~/src/providers/cognito/types'; +import { updateUserAttributes as updateUserAttributesClient } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { toAttributeType } from '~/src/providers/cognito/utils/apiHelpers'; +import { CodeDeliveryDetailsType } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { UpdateUserAttributesException } from '~/src/providers/cognito/types/errors'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Updates user's attributes while authenticated. @@ -32,7 +32,7 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export const updateUserAttributes = async ( - input: UpdateUserAttributesInput + input: UpdateUserAttributesInput, ): Promise => { const { userAttributes, options } = input; const authConfig = Amplify.getConfig().Auth?.Cognito; @@ -49,7 +49,7 @@ export const updateUserAttributes = async ( AccessToken: tokens.accessToken.toString(), ClientMetadata: clientMetadata, UserAttributes: toAttributeType(userAttributes), - } + }, ); return { @@ -59,7 +59,7 @@ export const updateUserAttributes = async ( }; function getConfirmedAttributes( - attributes: AuthUserAttributes + attributes: AuthUserAttributes, ): AuthUpdateUserAttributesOutput { const confirmedAttributes = {} as AuthUpdateUserAttributesOutput; Object.keys(attributes)?.forEach(key => { @@ -75,7 +75,7 @@ function getConfirmedAttributes( } function getUnConfirmedAttributes( - codeDeliveryDetailsList?: CodeDeliveryDetailsType[] + codeDeliveryDetailsList?: CodeDeliveryDetailsType[], ): AuthUpdateUserAttributesOutput { const unConfirmedAttributes = {} as AuthUpdateUserAttributesOutput; codeDeliveryDetailsList?.forEach(codeDeliveryDetails => { @@ -93,5 +93,6 @@ function getUnConfirmedAttributes( }, }; }); + return unConfirmedAttributes; } diff --git a/packages/auth/src/providers/cognito/apis/verifyTOTPSetup.ts b/packages/auth/src/providers/cognito/apis/verifyTOTPSetup.ts index 597a13750e1..ecad3a7fed9 100644 --- a/packages/auth/src/providers/cognito/apis/verifyTOTPSetup.ts +++ b/packages/auth/src/providers/cognito/apis/verifyTOTPSetup.ts @@ -1,19 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { VerifyTOTPSetupInput } from '../types'; -import { verifySoftwareToken } from '../utils/clients/CognitoIdentityProvider'; -import { VerifySoftwareTokenException } from '../types/errors'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { VerifyTOTPSetupInput } from '~/src/providers/cognito/types'; +import { verifySoftwareToken } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { VerifySoftwareTokenException } from '~/src/providers/cognito/types/errors'; import { Amplify, fetchAuthSession } from '@aws-amplify/core'; import { - assertTokenProviderConfig, AuthAction, + assertTokenProviderConfig, } from '@aws-amplify/core/internals/utils'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokens } from '../utils/types'; -import { getAuthUserAgentValue } from '../../../utils'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokens } from '~/src/providers/cognito/utils/types'; +import { getAuthUserAgentValue } from '~/src/utils'; /** * Verifies an OTP code retrieved from an associated authentication app. @@ -26,14 +26,14 @@ import { getAuthUserAgentValue } from '../../../utils'; * @throws AuthTokenConfigException - Thrown when the token provider config is invalid. */ export async function verifyTOTPSetup( - input: VerifyTOTPSetupInput + input: VerifyTOTPSetupInput, ): Promise { const authConfig = Amplify.getConfig().Auth?.Cognito; assertTokenProviderConfig(authConfig); const { code, options } = input; assertValidationError( !!code, - AuthValidationErrorCode.EmptyVerifyTOTPSetupCode + AuthValidationErrorCode.EmptyVerifyTOTPSetupCode, ); const { tokens } = await fetchAuthSession({ forceRefresh: false }); assertAuthTokens(tokens); @@ -46,6 +46,6 @@ export async function verifyTOTPSetup( AccessToken: tokens.accessToken.toString(), UserCode: code, FriendlyDeviceName: options?.friendlyDeviceName, - } + }, ); } diff --git a/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdProvider.ts b/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdProvider.ts index 1c8be32d077..5b7880b70c4 100644 --- a/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdProvider.ts +++ b/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdProvider.ts @@ -1,12 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthTokens, getId, ConsoleLogger } from '@aws-amplify/core'; +import { AuthTokens, ConsoleLogger, Identity, getId } from '@aws-amplify/core'; import { CognitoIdentityPoolConfig } from '@aws-amplify/core/internals/utils'; -import { AuthError } from '../../../errors/AuthError'; +import { AuthError } from '~/src/errors/AuthError'; +import { getRegionFromIdentityPoolId } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { GetIdInput } from '@aws-amplify/core/internals/aws-clients/cognitoIdentity'; + import { IdentityIdStore } from './types'; -import { getRegionFromIdentityPoolId } from '../utils/clients/CognitoIdentityProvider/utils'; -import { Identity } from '@aws-amplify/core'; import { formLoginsMap } from './utils'; const logger = new ConsoleLogger('CognitoIdentityIdProvider'); @@ -48,7 +49,7 @@ export async function cognitoIdentityIdProvider({ if (identityId && identityId.id === generatedIdentityId) { logger.debug( - `The guest identity ${identityId.id} has become the primary identity.` + `The guest identity ${identityId.id} has become the primary identity.`, ); } identityId = { @@ -70,12 +71,13 @@ export async function cognitoIdentityIdProvider({ // Store in-memory or local storage depending on guest or primary identityId identityIdStore.storeIdentityId(identityId); + return identityId.id; } async function generateIdentityId( - logins: {}, - authConfig: CognitoIdentityPoolConfig + logins: GetIdInput['Logins'], + authConfig: CognitoIdentityPoolConfig, ): Promise { const identityPoolId = authConfig?.identityPoolId; const region = getRegionFromIdentityPoolId(identityPoolId); @@ -92,7 +94,7 @@ async function generateIdentityId( { IdentityPoolId: identityPoolId, Logins: logins, - } + }, ) ).IdentityId; if (!idResult) { @@ -103,5 +105,6 @@ async function generateIdentityId( 'Make sure to pass a valid identityPoolId in the configuration.', }); } + return idResult; } diff --git a/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdStore.ts b/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdStore.ts index bda71efb5e3..cb7722a4d19 100644 --- a/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdStore.ts +++ b/packages/auth/src/providers/cognito/credentialsProvider/IdentityIdStore.ts @@ -8,9 +8,10 @@ import { KeyValueStorageInterface, } from '@aws-amplify/core'; import { assertIdentityPoolIdConfig } from '@aws-amplify/core/internals/utils'; +import { getAuthStorageKeys } from '~/src/providers/cognito/tokenProvider/TokenStore'; +import { AuthKeys } from '~/src/providers/cognito/tokenProvider/types'; + import { IdentityIdStorageKeys, IdentityIdStore } from './types'; -import { getAuthStorageKeys } from '../tokenProvider/TokenStore'; -import { AuthKeys } from '../tokenProvider/types'; const logger = new ConsoleLogger('DefaultIdentityIdStore'); @@ -26,9 +27,8 @@ export class DefaultIdentityIdStore implements IdentityIdStore { this.authConfig = authConfigParam; this._authKeys = createKeysForAuthStorage( 'Cognito', - authConfigParam.Cognito.identityPoolId + authConfigParam.Cognito.identityPoolId, ); - return; } constructor(keyValueStorage: KeyValueStorageInterface) { @@ -38,25 +38,27 @@ export class DefaultIdentityIdStore implements IdentityIdStore { async loadIdentityId(): Promise { assertIdentityPoolIdConfig(this.authConfig?.Cognito); try { - if (!!this._primaryIdentityId) { + if (this._primaryIdentityId) { return { id: this._primaryIdentityId, type: 'primary', }; } else { const storedIdentityId = await this.keyValueStorage.getItem( - this._authKeys.identityId + this._authKeys.identityId, ); - if (!!storedIdentityId) { + if (storedIdentityId) { return { id: storedIdentityId, type: 'guest', }; } + return null; } } catch (err) { logger.log('Error getting stored IdentityId.', err); + return null; } } @@ -84,6 +86,6 @@ export class DefaultIdentityIdStore implements IdentityIdStore { const createKeysForAuthStorage = (provider: string, identifier: string) => { return getAuthStorageKeys(IdentityIdStorageKeys)( `com.amplify.${provider}`, - identifier + identifier, ); }; diff --git a/packages/auth/src/providers/cognito/credentialsProvider/credentialsProvider.ts b/packages/auth/src/providers/cognito/credentialsProvider/credentialsProvider.ts index a2ef6bc38d9..bbec053bcfe 100644 --- a/packages/auth/src/providers/cognito/credentialsProvider/credentialsProvider.ts +++ b/packages/auth/src/providers/cognito/credentialsProvider/credentialsProvider.ts @@ -1,24 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { cognitoIdentityIdProvider } from './IdentityIdProvider'; import { AuthTokens, - CredentialsAndIdentityIdProvider, + ConsoleLogger, CredentialsAndIdentityId, - getCredentialsForIdentity, + CredentialsAndIdentityIdProvider, GetCredentialsOptions, - ConsoleLogger, + getCredentialsForIdentity, } from '@aws-amplify/core'; import { - assertIdentityPoolIdConfig, - decodeJWT, CognitoIdentityPoolConfig, + assertIdentityPoolIdConfig, } from '@aws-amplify/core/internals/utils'; -import { AuthError } from '../../../errors/AuthError'; +import { AuthError } from '~/src/errors/AuthError'; +import { getRegionFromIdentityPoolId } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertIdTokenInAuthTokens } from '~/src/providers/cognito/utils/types'; + import { IdentityIdStore } from './types'; -import { getRegionFromIdentityPoolId } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertIdTokenInAuthTokens } from '../utils/types'; +import { cognitoIdentityIdProvider } from './IdentityIdProvider'; import { formLoginsMap } from './utils'; const logger = new ConsoleLogger('CognitoCredentialsProvider'); @@ -36,7 +36,8 @@ export class CognitoAWSCredentialsAndIdentityIdProvider isAuthenticatedCreds: boolean; associatedIdToken?: string; }; - private _nextCredentialsRefresh: number = 0; + + private _nextCredentialsRefresh = 0; async clearCredentialsAndIdentityId(): Promise { logger.debug('Clearing out credentials and identityId'); @@ -50,11 +51,11 @@ export class CognitoAWSCredentialsAndIdentityIdProvider } async getCredentialsAndIdentityId( - getCredentialsOptions: GetCredentialsOptions + getCredentialsOptions: GetCredentialsOptions, ): Promise { const isAuthenticated = getCredentialsOptions.authenticated; - const tokens = getCredentialsOptions.tokens; - const authConfig = getCredentialsOptions.authConfig; + const { tokens } = getCredentialsOptions; + const { authConfig } = getCredentialsOptions; try { assertIdentityPoolIdConfig(authConfig?.Cognito); } catch { @@ -67,7 +68,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider return; } - const forceRefresh = getCredentialsOptions.forceRefresh; + const { forceRefresh } = getCredentialsOptions; const tokenHasChanged = this.hasTokenChanged(tokens); const identityId = await cognitoIdentityIdProvider({ tokens, @@ -83,13 +84,14 @@ export class CognitoAWSCredentialsAndIdentityIdProvider return this.getGuestCredentials(identityId, authConfig.Cognito); } else { assertIdTokenInAuthTokens(tokens); + return this.credsForOIDCTokens(authConfig.Cognito, tokens, identityId); } } private async getGuestCredentials( identityId: string, - authConfig: CognitoIdentityPoolConfig + authConfig: CognitoIdentityPoolConfig, ): Promise { // Return existing in-memory cached credentials only if it exists, is not past it's lifetime and is unauthenticated credentials if ( @@ -98,8 +100,9 @@ export class CognitoAWSCredentialsAndIdentityIdProvider this._credentialsAndIdentityId.isAuthenticatedCreds === false ) { logger.info( - 'returning stored credentials as they neither past TTL nor expired.' + 'returning stored credentials as they neither past TTL nor expired.', ); + return this._credentialsAndIdentityId; } @@ -116,7 +119,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider { region }, { IdentityId: identityId, - } + }, ); if ( @@ -159,7 +162,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider private async credsForOIDCTokens( authConfig: CognitoIdentityPoolConfig, authTokens: AuthTokens, - identityId: string + identityId: string, ): Promise { if ( this._credentialsAndIdentityId && @@ -167,8 +170,9 @@ export class CognitoAWSCredentialsAndIdentityIdProvider this._credentialsAndIdentityId.isAuthenticatedCreds === true ) { logger.debug( - 'returning stored credentials as they neither past TTL nor expired.' + 'returning stored credentials as they neither past TTL nor expired.', ); + return this._credentialsAndIdentityId; } @@ -186,7 +190,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider { IdentityId: identityId, Logins: logins, - } + }, ); if ( @@ -219,6 +223,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider type: 'primary', }); } + return res; } else { throw new AuthError({ @@ -233,6 +238,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider ? true : this._nextCredentialsRefresh <= Date.now(); } + private hasTokenChanged(tokens?: AuthTokens): boolean { return ( !!tokens && diff --git a/packages/auth/src/providers/cognito/credentialsProvider/index.ts b/packages/auth/src/providers/cognito/credentialsProvider/index.ts index c85150208e0..f7a626f4e45 100644 --- a/packages/auth/src/providers/cognito/credentialsProvider/index.ts +++ b/packages/auth/src/providers/cognito/credentialsProvider/index.ts @@ -1,22 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { defaultStorage } from '@aws-amplify/core'; +import { GetCredentialsForIdentityException } from '~/src/providers/cognito/types/errors'; + import { DefaultIdentityIdStore } from './IdentityIdStore'; import { CognitoAWSCredentialsAndIdentityIdProvider } from './credentialsProvider'; -import { defaultStorage } from '@aws-amplify/core'; /** - * Cognito specific implmentation of the CredentialsProvider interface + * Cognito specific implementation of the CredentialsProvider interface * that manages setting and getting of AWS Credentials. * - * @throws configuration expections: {@link InvalidIdentityPoolIdException } + * @throws configuration exceptions: {@link InvalidIdentityPoolIdException } * - Auth errors that may arise from misconfiguration. - * @throws service expections: {@link GetCredentialsForIdentityException}, {@link GetIdException} + * @throws service exception: {@link GetCredentialsForIdentityException}, {@link GetIdException} * */ export const cognitoCredentialsProvider = new CognitoAWSCredentialsAndIdentityIdProvider( - new DefaultIdentityIdStore(defaultStorage) + new DefaultIdentityIdStore(defaultStorage), ); export { CognitoAWSCredentialsAndIdentityIdProvider, DefaultIdentityIdStore }; diff --git a/packages/auth/src/providers/cognito/credentialsProvider/utils.ts b/packages/auth/src/providers/cognito/credentialsProvider/utils.ts index f0b5e15cb7c..b0b518f9cba 100644 --- a/packages/auth/src/providers/cognito/credentialsProvider/utils.ts +++ b/packages/auth/src/providers/cognito/credentialsProvider/utils.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { decodeJWT } from '@aws-amplify/core/internals/utils'; -import { AuthError } from '../../../errors/AuthError'; +import { AuthError } from '~/src/errors/AuthError'; export function formLoginsMap(idToken: string) { const issuer = decodeJWT(idToken).payload.iss; @@ -13,8 +13,9 @@ export function formLoginsMap(idToken: string) { message: 'Invalid Idtoken.', }); } - let domainName: string = issuer.replace(/(^\w+:|^)\/\//, ''); + const domainName: string = issuer.replace(/(^\w+:|^)\/\//, ''); res[domainName] = idToken; + return res; } diff --git a/packages/auth/src/providers/cognito/tokenProvider/CognitoUserPoolsTokenProvider.ts b/packages/auth/src/providers/cognito/tokenProvider/CognitoUserPoolsTokenProvider.ts index cae8a50fa36..4068afb8a29 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/CognitoUserPoolsTokenProvider.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/CognitoUserPoolsTokenProvider.ts @@ -8,7 +8,8 @@ import { KeyValueStorageInterface, defaultStorage, } from '@aws-amplify/core'; -import { refreshAuthTokens } from '../utils/refreshAuthTokens'; +import { refreshAuthTokens } from '~/src/providers/cognito/utils/refreshAuthTokens'; + import { DefaultTokenStore } from './TokenStore'; import { TokenOrchestrator } from './TokenOrchestrator'; import { CognitoUserPoolTokenProviderType } from './types'; @@ -25,8 +26,9 @@ export class CognitoUserPoolsTokenProvider this.tokenOrchestrator.setAuthTokenStore(this.authTokenStore); this.tokenOrchestrator.setTokenRefresher(refreshAuthTokens); } + getTokens( - { forceRefresh }: FetchAuthSessionOptions = { forceRefresh: false } + { forceRefresh }: FetchAuthSessionOptions = { forceRefresh: false }, ): Promise { return this.tokenOrchestrator.getTokens({ forceRefresh }); } @@ -34,9 +36,11 @@ export class CognitoUserPoolsTokenProvider setKeyValueStorage(keyValueStorage: KeyValueStorageInterface): void { this.authTokenStore.setKeyValueStorage(keyValueStorage); } + setWaitForInflightOAuth(waitForInflightOAuth: () => Promise): void { this.tokenOrchestrator.setWaitForInflightOAuth(waitForInflightOAuth); } + setAuthConfig(authConfig: AuthConfig) { this.authTokenStore.setAuthConfig(authConfig); this.tokenOrchestrator.setAuthConfig(authConfig); diff --git a/packages/auth/src/providers/cognito/tokenProvider/TokenOrchestrator.ts b/packages/auth/src/providers/cognito/tokenProvider/TokenOrchestrator.ts index 58b3b4dcad5..fe5aeea4df6 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/TokenOrchestrator.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/TokenOrchestrator.ts @@ -1,9 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { + AuthConfig, AuthTokens, FetchAuthSessionOptions, - AuthConfig, Hub, } from '@aws-amplify/core'; import { @@ -11,6 +11,10 @@ import { assertTokenProviderConfig, isTokenExpired, } from '@aws-amplify/core/internals/utils'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; +import { AuthError } from '~/src/errors/AuthError'; +import { CognitoAuthSignInDetails } from '~/src/providers/cognito/types'; + import { AuthTokenOrchestrator, AuthTokenStore, @@ -18,25 +22,27 @@ import { DeviceMetadata, TokenRefresher, } from './types'; -import { assertServiceError } from '../../../errors/utils/assertServiceError'; -import { AuthError } from '../../../errors/AuthError'; -import { CognitoAuthSignInDetails } from '../types'; export class TokenOrchestrator implements AuthTokenOrchestrator { private authConfig?: AuthConfig; tokenStore?: AuthTokenStore; tokenRefresher?: TokenRefresher; - waitForInflightOAuth: () => Promise = async () => {}; + waitForInflightOAuth: () => Promise = async () => { + // no-op + }; setAuthConfig(authConfig: AuthConfig) { this.authConfig = authConfig; } + setTokenRefresher(tokenRefresher: TokenRefresher) { this.tokenRefresher = tokenRefresher; } + setAuthTokenStore(tokenStore: AuthTokenStore) { this.tokenStore = tokenStore; } + setWaitForInflightOAuth(waitForInflightOAuth: () => Promise) { this.waitForInflightOAuth = waitForInflightOAuth; } @@ -48,6 +54,7 @@ export class TokenOrchestrator implements AuthTokenOrchestrator { message: 'TokenStore not set', }); } + return this.tokenStore; } @@ -58,11 +65,12 @@ export class TokenOrchestrator implements AuthTokenOrchestrator { message: 'TokenRefresher not set', }); } + return this.tokenRefresher; } async getTokens( - options?: FetchAuthSessionOptions + options?: FetchAuthSessionOptions, ): Promise< (AuthTokens & { signInDetails?: CognitoAuthSignInDetails }) | null > { @@ -146,11 +154,12 @@ export class TokenOrchestrator implements AuthTokenOrchestrator { 'auth', { event: 'tokenRefresh_failure' }, 'Auth', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); throw err; } } + async setTokens({ tokens }: { tokens: CognitoAuthTokens }) { return this.getTokenStore().storeTokens(tokens); } @@ -162,6 +171,7 @@ export class TokenOrchestrator implements AuthTokenOrchestrator { getDeviceMetadata(username?: string): Promise { return this.getTokenStore().getDeviceMetadata(username); } + clearDeviceMetadata(username?: string): Promise { return this.getTokenStore().clearDeviceMetadata(username); } diff --git a/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts b/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts index 29adeb9eb43..fd65ab541c2 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/TokenStore.ts @@ -5,6 +5,8 @@ import { assertTokenProviderConfig, decodeJWT, } from '@aws-amplify/core/internals/utils'; +import { AuthError } from '~/src/errors/AuthError'; + import { AuthKeys, AuthTokenStorageKeys, @@ -12,8 +14,7 @@ import { CognitoAuthTokens, DeviceMetadata, } from './types'; -import { AuthError } from '../../../errors/AuthError'; -import { assert, TokenProviderErrorCode } from './errorHelpers'; +import { TokenProviderErrorCode, assert } from './errorHelpers'; export class DefaultTokenStore implements AuthTokenStore { private authConfig?: AuthConfig; @@ -26,11 +27,14 @@ export class DefaultTokenStore implements AuthTokenStore { message: 'KeyValueStorage was not found in TokenStore', }); } + return this.keyValueStorage; } + setKeyValueStorage(keyValueStorage: KeyValueStorageInterface) { this.keyValueStorage = keyValueStorage; } + setAuthConfig(authConfig: AuthConfig) { this.authConfig = authConfig; } @@ -41,7 +45,7 @@ export class DefaultTokenStore implements AuthTokenStore { try { const authKeys = await this.getAuthKeys(); const accessTokenString = await this.getKeyValueStorage().getItem( - authKeys.accessToken + authKeys.accessToken, ); if (!accessTokenString) { @@ -53,7 +57,7 @@ export class DefaultTokenStore implements AuthTokenStore { const accessToken = decodeJWT(accessTokenString); const itString = await this.getKeyValueStorage().getItem( - authKeys.idToken + authKeys.idToken, ); const idToken = itString ? decodeJWT(itString) : undefined; @@ -66,7 +70,7 @@ export class DefaultTokenStore implements AuthTokenStore { const clockDrift = Number.parseInt(clockDriftString); const signInDetails = await this.getKeyValueStorage().getItem( - authKeys.signInDetails + authKeys.signInDetails, ); const tokens: CognitoAuthTokens = { accessToken, @@ -80,11 +84,13 @@ export class DefaultTokenStore implements AuthTokenStore { if (signInDetails) { tokens.signInDetails = JSON.parse(signInDetails); } + return tokens; } catch (err) { return null; } } + async storeTokens(tokens: CognitoAuthTokens): Promise { assert(tokens !== undefined, TokenProviderErrorCode.InvalidAuthTokens); await this.clearTokens(); @@ -92,57 +98,57 @@ export class DefaultTokenStore implements AuthTokenStore { const lastAuthUser = tokens.username; await this.getKeyValueStorage().setItem( this.getLastAuthUserKey(), - lastAuthUser + lastAuthUser, ); const authKeys = await this.getAuthKeys(); await this.getKeyValueStorage().setItem( authKeys.accessToken, - tokens.accessToken.toString() + tokens.accessToken.toString(), ); - if (!!tokens.idToken) { + if (tokens.idToken) { await this.getKeyValueStorage().setItem( authKeys.idToken, - tokens.idToken.toString() + tokens.idToken.toString(), ); } - if (!!tokens.refreshToken) { + if (tokens.refreshToken) { await this.getKeyValueStorage().setItem( authKeys.refreshToken, - tokens.refreshToken + tokens.refreshToken, ); } - if (!!tokens.deviceMetadata) { + if (tokens.deviceMetadata) { if (tokens.deviceMetadata.deviceKey) { await this.getKeyValueStorage().setItem( authKeys.deviceKey, - tokens.deviceMetadata.deviceKey + tokens.deviceMetadata.deviceKey, ); } if (tokens.deviceMetadata.deviceGroupKey) { await this.getKeyValueStorage().setItem( authKeys.deviceGroupKey, - tokens.deviceMetadata.deviceGroupKey + tokens.deviceMetadata.deviceGroupKey, ); } await this.getKeyValueStorage().setItem( authKeys.randomPasswordKey, - tokens.deviceMetadata.randomPassword + tokens.deviceMetadata.randomPassword, ); } - if (!!tokens.signInDetails) { + if (tokens.signInDetails) { await this.getKeyValueStorage().setItem( authKeys.signInDetails, - JSON.stringify(tokens.signInDetails) + JSON.stringify(tokens.signInDetails), ); } await this.getKeyValueStorage().setItem( authKeys.clockDrift, - `${tokens.clockDrift}` + `${tokens.clockDrift}`, ); } @@ -162,16 +168,16 @@ export class DefaultTokenStore implements AuthTokenStore { async getDeviceMetadata(username?: string): Promise { const authKeys = await this.getAuthKeys(username); const deviceKey = await this.getKeyValueStorage().getItem( - authKeys.deviceKey + authKeys.deviceKey, ); const deviceGroupKey = await this.getKeyValueStorage().getItem( - authKeys.deviceGroupKey + authKeys.deviceGroupKey, ); const randomPassword = await this.getKeyValueStorage().getItem( - authKeys.randomPasswordKey + authKeys.randomPasswordKey, ); - return !!randomPassword + return randomPassword ? { deviceKey: deviceKey ?? undefined, deviceGroupKey: deviceGroupKey ?? undefined, @@ -179,6 +185,7 @@ export class DefaultTokenStore implements AuthTokenStore { } : null; } + async clearDeviceMetadata(username?: string): Promise { const authKeys = await this.getAuthKeys(username); await Promise.all([ @@ -189,19 +196,21 @@ export class DefaultTokenStore implements AuthTokenStore { } private async getAuthKeys( - username?: string + username?: string, ): Promise> { assertTokenProviderConfig(this.authConfig?.Cognito); const lastAuthUser = username ?? (await this.getLastAuthUser()); + return createKeysForAuthStorage( this.name, - `${this.authConfig.Cognito.userPoolClientId}.${lastAuthUser}` + `${this.authConfig.Cognito.userPoolClientId}.${lastAuthUser}`, ); } private getLastAuthUserKey() { assertTokenProviderConfig(this.authConfig?.Cognito); const identifier = this.authConfig.Cognito.userPoolClientId; + return `${this.name}.${identifier}.LastAuthUser`; } @@ -216,21 +225,22 @@ export class DefaultTokenStore implements AuthTokenStore { export const createKeysForAuthStorage = ( provider: string, - identifier: string + identifier: string, ) => { return getAuthStorageKeys(AuthTokenStorageKeys)(`${provider}`, identifier); }; export function getAuthStorageKeys>( - authKeys: T + authKeys: T, ) { const keys = Object.values({ ...authKeys }); + return (prefix: string, identifier: string) => keys.reduce( (acc, authKey) => ({ ...acc, [authKey]: `${prefix}.${identifier}.${authKey}`, }), - {} as AuthKeys + {} as AuthKeys, ); } diff --git a/packages/auth/src/providers/cognito/tokenProvider/cacheTokens.ts b/packages/auth/src/providers/cognito/tokenProvider/cacheTokens.ts index acd5ee07f47..72d50c49736 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/cacheTokens.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/cacheTokens.ts @@ -1,17 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { AmplifyError, decodeJWT } from '@aws-amplify/core/internals/utils'; -import { CognitoAuthSignInDetails } from '../types'; -import { AuthenticationResultType } from '../utils/clients/CognitoIdentityProvider/types'; -import { tokenOrchestrator } from './tokenProvider'; +import { AuthenticationResultType } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/types'; +import { CognitoAuthSignInDetails } from '~/src/providers/cognito/types'; + import { CognitoAuthTokens, DeviceMetadata } from './types'; +import { tokenOrchestrator } from './tokenProvider'; export async function cacheCognitoTokens( AuthenticationResult: AuthenticationResultType & { NewDeviceMetadata?: DeviceMetadata; username: string; signInDetails?: CognitoAuthSignInDetails; - } + }, ): Promise { if (AuthenticationResult.AccessToken) { const accessToken = decodeJWT(AuthenticationResult.AccessToken); diff --git a/packages/auth/src/providers/cognito/tokenProvider/tokenProvider.ts b/packages/auth/src/providers/cognito/tokenProvider/tokenProvider.ts index 7e5135395f4..900e374fecb 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/tokenProvider.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/tokenProvider.ts @@ -6,5 +6,4 @@ import { CognitoUserPoolsTokenProvider } from './CognitoUserPoolsTokenProvider'; export const cognitoUserPoolsTokenProvider = new CognitoUserPoolsTokenProvider(); -export const tokenOrchestrator = - cognitoUserPoolsTokenProvider.tokenOrchestrator; +export const { tokenOrchestrator } = cognitoUserPoolsTokenProvider; diff --git a/packages/auth/src/providers/cognito/tokenProvider/types.ts b/packages/auth/src/providers/cognito/tokenProvider/types.ts index 45fd29ada1e..f9e97bfb715 100644 --- a/packages/auth/src/providers/cognito/tokenProvider/types.ts +++ b/packages/auth/src/providers/cognito/tokenProvider/types.ts @@ -7,7 +7,7 @@ import { KeyValueStorageInterface, TokenProvider, } from '@aws-amplify/core'; -import { CognitoAuthSignInDetails } from '../types'; +import { CognitoAuthSignInDetails } from '~/src/providers/cognito/types'; export type TokenRefresher = ({ tokens, @@ -48,20 +48,20 @@ export interface AuthTokenStore { export interface AuthTokenOrchestrator { setTokenRefresher(tokenRefresher: TokenRefresher): void; setAuthTokenStore(tokenStore: AuthTokenStore): void; - getTokens: ( - options?: FetchAuthSessionOptions - ) => Promise< + getTokens( + options?: FetchAuthSessionOptions, + ): Promise< (AuthTokens & { signInDetails?: CognitoAuthSignInDetails }) | null >; - setTokens: ({ tokens }: { tokens: CognitoAuthTokens }) => Promise; - clearTokens: () => Promise; + setTokens({ tokens }: { tokens: CognitoAuthTokens }): Promise; + clearTokens(): Promise; getDeviceMetadata(username?: string): Promise; clearDeviceMetadata(username?: string): Promise; } export interface CognitoUserPoolTokenProviderType extends TokenProvider { - setKeyValueStorage: (keyValueStorage: KeyValueStorageInterface) => void; - setAuthConfig: (authConfig: AuthConfig) => void; + setKeyValueStorage(keyValueStorage: KeyValueStorageInterface): void; + setAuthConfig(authConfig: AuthConfig): void; } export type CognitoAuthTokens = AuthTokens & { @@ -72,8 +72,8 @@ export type CognitoAuthTokens = AuthTokens & { signInDetails?: CognitoAuthSignInDetails; }; -export type DeviceMetadata = { +export interface DeviceMetadata { deviceKey?: string; deviceGroupKey?: string; randomPassword: string; -}; +} diff --git a/packages/auth/src/providers/cognito/types/inputs.ts b/packages/auth/src/providers/cognito/types/inputs.ts index 000d9b444ae..4e941a5643c 100644 --- a/packages/auth/src/providers/cognito/types/inputs.ts +++ b/packages/auth/src/providers/cognito/types/inputs.ts @@ -3,40 +3,40 @@ import { AuthVerifiableAttributeKey } from '@aws-amplify/core/internals/utils'; import { - MFAPreference, ConfirmResetPasswordOptions, ConfirmSignInOptions, ConfirmSignUpOptions, - UserAttributeKey, - VerifiableUserAttributeKey, + MFAPreference, ResendSignUpCodeOptions, ResetPasswordOptions, + SendUserAttributeVerificationCodeOptions, SignInOptions, SignUpOptions, - UpdateUserAttributesOptions, UpdateUserAttributeOptions, + UpdateUserAttributesOptions, + UserAttributeKey, + VerifiableUserAttributeKey, VerifyTOTPSetupOptions, - SendUserAttributeVerificationCodeOptions, -} from '../types'; +} from '~/src/providers/cognito/types'; import { AuthConfirmResetPasswordInput, AuthConfirmSignInInput, AuthConfirmSignUpInput, AuthConfirmUserAttributeInput, + AuthDeleteUserAttributesInput, + AuthForgetDeviceInput, AuthResendSignUpCodeInput, AuthResetPasswordInput, + AuthSendUserAttributeVerificationCodeInput, AuthSignInInput, AuthSignInWithRedirectInput, AuthSignOutInput, AuthSignUpInput, AuthUpdatePasswordInput, - AuthUpdateUserAttributesInput, AuthUpdateUserAttributeInput, + AuthUpdateUserAttributesInput, AuthVerifyTOTPSetupInput, - AuthSendUserAttributeVerificationCodeInput, - AuthDeleteUserAttributesInput, - AuthForgetDeviceInput, -} from '../../../types'; +} from '~/src/types'; /** * Input type for Cognito confirmResetPassword API. @@ -114,10 +114,10 @@ export type SignUpInput = AuthSignUpInput>; /** * Input type for Cognito updateMFAPreference API. */ -export type UpdateMFAPreferenceInput = { +export interface UpdateMFAPreferenceInput { sms?: MFAPreference; totp?: MFAPreference; -}; +} /** * Input type for Cognito updatePassword API. diff --git a/packages/auth/src/providers/cognito/types/models.ts b/packages/auth/src/providers/cognito/types/models.ts index ae9dbc90acd..ec8adc8d48d 100644 --- a/packages/auth/src/providers/cognito/types/models.ts +++ b/packages/auth/src/providers/cognito/types/models.ts @@ -5,15 +5,14 @@ import { AuthStandardAttributeKey, AuthVerifiableAttributeKey, } from '@aws-amplify/core/internals/utils'; - import { - AuthUserAttribute, - AuthDevice, AWSAuthUser, AuthCodeDeliveryDetails, -} from '../../../types'; + AuthDevice, + AuthUserAttribute, +} from '~/src/types'; +import { AuthProvider } from '~/src/types/inputs'; -import { AuthProvider } from '../../../types/inputs'; import { SignUpOutput } from './outputs'; /** @@ -36,9 +35,7 @@ export const cognitoHostedUIIdentityProviderMap: Record = /** * Arbitrary key/value pairs that may be passed as part of certain Cognito requests */ -export type ClientMetadata = { - [key: string]: string; -}; +export type ClientMetadata = Record; /** * The user attribute types available for Cognito. @@ -54,12 +51,12 @@ export type VerifiableUserAttributeKey = AuthVerifiableAttributeKey; * Cognito custom attribute type */ // TODO(V6): replace by `custom:${string}` once categories that use auth have upgraded TS -export type CustomAttribute = string & {}; +export type CustomAttribute = string; /** * One or more name-value pairs containing the validation data in the request to register a user. */ -export type ValidationData = { [key: string]: string }; +export type ValidationData = Record; /** * Cognito supported MFAPreference values that may be passed as part of the UpdateMFAPreferenceRequest. @@ -91,10 +88,10 @@ export type AWSAuthDevice = AuthDevice & { /** * Holds the sign in details of the user. */ -export type CognitoAuthSignInDetails = { +export interface CognitoAuthSignInDetails { loginId?: string; authFlowType?: AuthFlowType; -}; +} /** * Holds the user information along with the sign in details. diff --git a/packages/auth/src/providers/cognito/types/options.ts b/packages/auth/src/providers/cognito/types/options.ts index 3daeb7b3cba..dc0e7a721a0 100644 --- a/packages/auth/src/providers/cognito/types/options.ts +++ b/packages/auth/src/providers/cognito/types/options.ts @@ -4,10 +4,11 @@ import { AuthServiceOptions, AuthSignUpOptions, - AuthUserAttributes, AuthUserAttributeKey, -} from '../../../types'; -import { ClientMetadata, AuthFlowType, ValidationData } from './models'; + AuthUserAttributes, +} from '~/src/types'; + +import { AuthFlowType, ClientMetadata, ValidationData } from './models'; /** * Options specific to Cognito Confirm Reset Password. diff --git a/packages/auth/src/providers/cognito/types/outputs.ts b/packages/auth/src/providers/cognito/types/outputs.ts index aa6a40c58be..53e46005f70 100644 --- a/packages/auth/src/providers/cognito/types/outputs.ts +++ b/packages/auth/src/providers/cognito/types/outputs.ts @@ -3,22 +3,26 @@ import { AuthVerifiableAttributeKey } from '@aws-amplify/core/internals/utils'; import { - AuthMFAType, - AuthUserAttributes, AuthCodeDeliveryDetails, - AuthTOTPSetupDetails, + AuthMFAType, + AuthResetPasswordOutput, AuthSignInOutput, AuthSignUpOutput, - AuthResetPasswordOutput, - AuthUpdateUserAttributesOutput, + AuthTOTPSetupDetails, AuthUpdateUserAttributeOutput, -} from '../../../types'; -import { AWSAuthDevice, AuthUser, UserAttributeKey } from '../types'; + AuthUpdateUserAttributesOutput, + AuthUserAttributes, +} from '~/src/types'; +import { + AWSAuthDevice, + AuthUser, + UserAttributeKey, +} from '~/src/providers/cognito/types'; -export type FetchMFAPreferenceOutput = { +export interface FetchMFAPreferenceOutput { enabled?: AuthMFAType[]; preferred?: AuthMFAType; -}; +} /** * Output type for Cognito fetchUserAttributes API. diff --git a/packages/auth/src/providers/cognito/utils/apiHelpers.ts b/packages/auth/src/providers/cognito/utils/apiHelpers.ts index 9b0dfe72519..ce376e1118d 100644 --- a/packages/auth/src/providers/cognito/utils/apiHelpers.ts +++ b/packages/auth/src/providers/cognito/utils/apiHelpers.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthUserAttributes } from '../../../types'; +import { AuthUserAttributes } from '~/src/types'; + import { AttributeType } from './clients/CognitoIdentityProvider/types'; /** @@ -10,7 +11,7 @@ import { AttributeType } from './clients/CognitoIdentityProvider/types'; * @returns an array of AttributeType objects. */ export function toAttributeType>( - attributes: T + attributes: T, ): AttributeType[] { return Object.entries(attributes).map(([key, value]) => ({ Name: key, @@ -25,11 +26,12 @@ export function toAttributeType>( * @returns AuthUserAttributes object. */ export function toAuthUserAttribute( - attributes?: AttributeType[] + attributes?: AttributeType[], ): AuthUserAttributes { const userAttributes: AuthUserAttributes = {}; attributes?.forEach(attribute => { if (attribute.Name) userAttributes[attribute.Name] = attribute.Value; }); + return userAttributes; } diff --git a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/base.ts b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/base.ts index 21a96c4d429..0704f8fe7d3 100644 --- a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/base.ts +++ b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/base.ts @@ -10,14 +10,14 @@ import { HttpResponse, Middleware, getDnsSuffix, - unauthenticatedHandler, - parseJsonError, getRetryDecider, jitteredBackoff, + parseJsonError, + unauthenticatedHandler, } from '@aws-amplify/core/internals/aws-client-utils'; import { - getAmplifyUserAgent, AmplifyUrl, + getAmplifyUserAgent, } from '@aws-amplify/core/internals/utils'; import { composeTransferHandler } from '@aws-amplify/core/internals/aws-client-utils/composers'; @@ -33,7 +33,7 @@ const endpointResolver = ({ region }: EndpointResolverOptions) => { const authConfig = Amplify.getConfig().Auth?.Cognito; const customURL = authConfig?.userPoolEndpoint; const defaultURL = new AmplifyUrl( - `https://${SERVICE_NAME}.${region}.${getDnsSuffix(region)}` + `https://${SERVICE_NAME}.${region}.${getDnsSuffix(region)}`, ); return { @@ -44,12 +44,12 @@ const endpointResolver = ({ region }: EndpointResolverOptions) => { /** * A Cognito Identity-specific middleware that disables caching for all requests. */ -const disableCacheMiddleware: Middleware = - () => (next, context) => - async function disableCacheMiddleware(request) { - request.headers['cache-control'] = 'no-store'; - return next(request); - }; +const disableCacheMiddleware: Middleware = + () => next => async request => { + request.headers['cache-control'] = 'no-store'; + + return next(request); + }; /** * A Cognito Identity-specific transfer handler that does NOT sign requests, and @@ -90,7 +90,7 @@ export const getSharedHeaders = (operation: string): Headers => ({ export const buildHttpRpcRequest = ( { url }: Endpoint, headers: Headers, - body: string + body: string, ): HttpRequest => ({ headers, url, diff --git a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/index.ts b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/index.ts index f0ec6fc21b1..df04e930c12 100644 --- a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/index.ts +++ b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/index.ts @@ -1,74 +1,79 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 + +// TODO(eslint): remove this linter suppression. +/* eslint-disable @typescript-eslint/no-empty-interface */ +import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; +import { + Endpoint, + HttpRequest, + HttpResponse, + parseJsonBody, + parseJsonError, +} from '@aws-amplify/core/internals/aws-client-utils'; +import { assertServiceError } from '~/src/errors/utils/assertServiceError'; +import { AuthError } from '~/src/errors/AuthError'; + +import { + buildHttpRpcRequest, + cognitoUserPoolTransferHandler, + defaultConfig, + getSharedHeaders, +} from './base'; + import type { - ConfirmForgotPasswordCommandInput as ConfirmForgotPasswordInput, - ConfirmForgotPasswordCommandOutput as ConfirmForgotPasswordOutput, - ForgotPasswordCommandInput as ForgotPasswordInput, - ForgotPasswordCommandOutput as ForgotPasswordOutput, - ResendConfirmationCodeCommandInput as ResendConfirmationCodeInput, - ResendConfirmationCodeCommandOutput as ResendConfirmationCodeOutput, - SignUpCommandInput as SignUpInput, - SignUpCommandOutput as SignUpOutput, - InitiateAuthCommandInput as InitiateAuthInput, - InitiateAuthCommandOutput as InitiateAuthOutput, - RespondToAuthChallengeCommandInput as RespondToAuthChallengeInput, - RespondToAuthChallengeCommandOutput as RespondToAuthChallengeOutput, - ConfirmSignUpCommandOutput as ConfirmSignUpOutput, - ConfirmSignUpCommandInput as ConfirmSignUpInput, - VerifySoftwareTokenCommandInput as VerifySoftwareTokenInput, - VerifySoftwareTokenCommandOutput as VerifySoftwareTokenOutput, AssociateSoftwareTokenCommandInput as AssociateSoftwareTokenInput, AssociateSoftwareTokenCommandOutput as AssociateSoftwareTokenOutput, - SetUserMFAPreferenceCommandInput as SetUserMFAPreferenceInput, - SetUserMFAPreferenceCommandOutput as SetUserMFAPreferenceOutput, - GetUserCommandInput as GetUserInput, - GetUserCommandOutput as GetUserOutput, ChangePasswordCommandInput as ChangePasswordInput, ChangePasswordCommandOutput as ChangePasswordOutput, ConfirmDeviceCommandInput as ConfirmDeviceInput, ConfirmDeviceCommandOutput as ConfirmDeviceOutput, - ForgetDeviceCommandInput as ForgetDeviceInput, - ForgetDeviceCommandOutput as ForgetDeviceOutput, + ConfirmForgotPasswordCommandInput as ConfirmForgotPasswordInput, + ConfirmForgotPasswordCommandOutput as ConfirmForgotPasswordOutput, + ConfirmSignUpCommandInput as ConfirmSignUpInput, + ConfirmSignUpCommandOutput as ConfirmSignUpOutput, + DeleteUserAttributesCommandInput as DeleteUserAttributesInput, + DeleteUserAttributesCommandOutput as DeleteUserAttributesOutput, DeleteUserCommandInput as DeleteUserInput, DeleteUserCommandOutput as DeleteUserOutput, + ForgetDeviceCommandInput as ForgetDeviceInput, + ForgetDeviceCommandOutput as ForgetDeviceOutput, + ForgotPasswordCommandInput as ForgotPasswordInput, + ForgotPasswordCommandOutput as ForgotPasswordOutput, GetUserAttributeVerificationCodeCommandInput as GetUserAttributeVerificationCodeInput, GetUserAttributeVerificationCodeCommandOutput as GetUserAttributeVerificationCodeOutput, + GetUserCommandInput as GetUserInput, + GetUserCommandOutput as GetUserOutput, GlobalSignOutCommandInput as GlobalSignOutInput, GlobalSignOutCommandOutput as GlobalSignOutOutput, + InitiateAuthCommandInput as InitiateAuthInput, + InitiateAuthCommandOutput as InitiateAuthOutput, + ListDevicesCommandInput as ListDevicesInput, + ListDevicesCommandOutput as ListDevicesOutput, + ResendConfirmationCodeCommandInput as ResendConfirmationCodeInput, + ResendConfirmationCodeCommandOutput as ResendConfirmationCodeOutput, + RespondToAuthChallengeCommandInput as RespondToAuthChallengeInput, + RespondToAuthChallengeCommandOutput as RespondToAuthChallengeOutput, + SetUserMFAPreferenceCommandInput as SetUserMFAPreferenceInput, + SetUserMFAPreferenceCommandOutput as SetUserMFAPreferenceOutput, + SignUpCommandInput as SignUpInput, + SignUpCommandOutput as SignUpOutput, + UpdateDeviceStatusCommandInput as UpdateDeviceStatusInput, + UpdateDeviceStatusCommandOutput as UpdateDeviceStatusOutput, UpdateUserAttributesCommandInput as UpdateUserAttributesInput, UpdateUserAttributesCommandOutput as UpdateUserAttributesOutput, + VerifySoftwareTokenCommandInput as VerifySoftwareTokenInput, + VerifySoftwareTokenCommandOutput as VerifySoftwareTokenOutput, VerifyUserAttributeCommandInput as VerifyUserAttributeInput, VerifyUserAttributeCommandOutput as VerifyUserAttributeOutput, - UpdateDeviceStatusCommandInput as UpdateDeviceStatusInput, - UpdateDeviceStatusCommandOutput as UpdateDeviceStatusOutput, - ListDevicesCommandInput as ListDevicesInput, - ListDevicesCommandOutput as ListDevicesOutput, - DeleteUserAttributesCommandInput as DeleteUserAttributesInput, - DeleteUserAttributesCommandOutput as DeleteUserAttributesOutput, } from './types'; -import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import { - buildHttpRpcRequest, - cognitoUserPoolTransferHandler, - defaultConfig, - getSharedHeaders, -} from './base'; -import { - Endpoint, - HttpRequest, - HttpResponse, - parseJsonBody, - parseJsonError, -} from '@aws-amplify/core/internals/aws-client-utils'; -import { assertServiceError } from '../../../../../errors/utils/assertServiceError'; -import { AuthError } from '../../../../../errors/AuthError'; -type RevokeTokenInput = { +interface RevokeTokenInput { Token: string; ClientId: string; -}; +} -type RevokeTokenOutput = {}; +interface RevokeTokenOutput {} type ClientOperation = | 'SignUp' @@ -100,11 +105,12 @@ const buildUserPoolSerializer = (input: Input, endpoint: Endpoint): HttpRequest => { const headers = getSharedHeaders(operation); const body = JSON.stringify(input); + return buildHttpRpcRequest(endpoint, headers, body); }; const buildUserPoolDeserializer = (): (( - response: HttpResponse + response: HttpResponse, ) => Promise) => { return async (response: HttpResponse): Promise => { if (response.statusCode >= 300) { @@ -113,13 +119,14 @@ const buildUserPoolDeserializer = (): (( throw new AuthError({ name: error.name, message: error.message }); } else { const body = await parseJsonBody(response); + return body; } }; }; const handleEmptyResponseDeserializer = (): (( - response: HttpResponse + response: HttpResponse, ) => Promise) => { return async (response: HttpResponse): Promise => { if (response.statusCode >= 300) { @@ -136,147 +143,147 @@ export const initiateAuth = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('InitiateAuth'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const revokeToken = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('RevokeToken'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const signUp = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('SignUp'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const confirmSignUp = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ConfirmSignUp'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const forgotPassword = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ForgotPassword'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const confirmForgotPassword = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ConfirmForgotPassword'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const respondToAuthChallenge = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer( - 'RespondToAuthChallenge' + 'RespondToAuthChallenge', ), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const resendConfirmationCode = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer( - 'ResendConfirmationCode' + 'ResendConfirmationCode', ), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const verifySoftwareToken = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('VerifySoftwareToken'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const associateSoftwareToken = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer( - 'AssociateSoftwareToken' + 'AssociateSoftwareToken', ), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const setUserMFAPreference = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('SetUserMFAPreference'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const getUser = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('GetUser'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const changePassword = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ChangePassword'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const confirmDevice = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ConfirmDevice'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const forgetDevice = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ForgetDevice'), handleEmptyResponseDeserializer(), - defaultConfig + defaultConfig, ); export const deleteUser = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('DeleteUser'), handleEmptyResponseDeserializer(), - defaultConfig + defaultConfig, ); export const getUserAttributeVerificationCode = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer( - 'GetUserAttributeVerificationCode' + 'GetUserAttributeVerificationCode', ), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const globalSignOut = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('GlobalSignOut'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const updateUserAttributes = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('UpdateUserAttributes'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const verifyUserAttribute = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('VerifyUserAttribute'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const updateDeviceStatus = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('UpdateDeviceStatus'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const listDevices = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('ListDevices'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); export const deleteUserAttributes = composeServiceApi( cognitoUserPoolTransferHandler, buildUserPoolSerializer('DeleteUserAttributes'), buildUserPoolDeserializer(), - defaultConfig + defaultConfig, ); diff --git a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/types.ts b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/types.ts index 866ec6321e5..62df33cea97 100644 --- a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/types.ts +++ b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/types.ts @@ -3,7 +3,8 @@ // Generated by scripts/dts-bundler/README.md -/* tslint:disable */ +// TODO(eslint): remove this linter suppression. +/* eslint-disable @typescript-eslint/no-empty-interface */ import { MetadataBearer as __MetadataBearer } from '@aws-sdk/types'; @@ -28,14 +29,14 @@ export type ChallengeParameters = { PASSWORD_CLAIM_SIGNATURE?: string; MFAS_CAN_CHOOSE?: string; MFAS_CAN_SETUP?: string; -} & { [Params: string]: unknown }; +} & Record; export type CognitoMFAType = 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA'; -export type CognitoMFASettings = { +export interface CognitoMFASettings { Enabled?: boolean; PreferredMfa?: boolean; -}; +} declare enum AuthFlowType { ADMIN_NO_SRP_AUTH = 'ADMIN_NO_SRP_AUTH', @@ -201,7 +202,7 @@ declare namespace GetUserAttributeVerificationCodeRequest { * @internal */ const filterSensitiveLog: ( - obj: GetUserAttributeVerificationCodeRequest + obj: GetUserAttributeVerificationCodeRequest, ) => any; } declare namespace GetUserAttributeVerificationCodeResponse { @@ -209,7 +210,7 @@ declare namespace GetUserAttributeVerificationCodeResponse { * @internal */ const filterSensitiveLog: ( - obj: GetUserAttributeVerificationCodeResponse + obj: GetUserAttributeVerificationCodeResponse, ) => any; } declare namespace GetUserRequest { @@ -411,8 +412,7 @@ export interface AnalyticsMetadataType { */ AnalyticsEndpointId?: string; } -export interface AssociateSoftwareTokenCommandInput - extends AssociateSoftwareTokenRequest {} +export type AssociateSoftwareTokenCommandInput = AssociateSoftwareTokenRequest; export interface AssociateSoftwareTokenCommandOutput extends AssociateSoftwareTokenResponse, __MetadataBearer {} @@ -478,7 +478,7 @@ export interface AuthenticationResultType { */ NewDeviceMetadata?: NewDeviceMetadataType; } -export interface ChangePasswordCommandInput extends ChangePasswordRequest {} +export type ChangePasswordCommandInput = ChangePasswordRequest; export interface ChangePasswordCommandOutput extends ChangePasswordResponse, __MetadataBearer {} @@ -520,7 +520,7 @@ export interface CodeDeliveryDetailsType { */ AttributeName?: string; } -export interface ConfirmDeviceCommandInput extends ConfirmDeviceRequest {} +export type ConfirmDeviceCommandInput = ConfirmDeviceRequest; export interface ConfirmDeviceCommandOutput extends ConfirmDeviceResponse, __MetadataBearer {} @@ -554,8 +554,7 @@ export interface ConfirmDeviceResponse { */ UserConfirmationNecessary?: boolean; } -export interface ConfirmForgotPasswordCommandInput - extends ConfirmForgotPasswordRequest {} +export type ConfirmForgotPasswordCommandInput = ConfirmForgotPasswordRequest; export interface ConfirmForgotPasswordCommandOutput extends ConfirmForgotPasswordResponse, __MetadataBearer {} @@ -620,15 +619,13 @@ export interface ConfirmForgotPasswordRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

The response from the server that results from a user's request to retrieve a forgotten password.

*/ export interface ConfirmForgotPasswordResponse {} -export interface ConfirmSignUpCommandInput extends ConfirmSignUpRequest {} +export type ConfirmSignUpCommandInput = ConfirmSignUpRequest; export interface ConfirmSignUpCommandOutput extends ConfirmSignUpResponse, __MetadataBearer {} @@ -695,15 +692,13 @@ export interface ConfirmSignUpRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

Represents the response from the server for the registration confirmation.

*/ export interface ConfirmSignUpResponse {} -export interface DeleteUserCommandInput extends DeleteUserRequest {} +export type DeleteUserCommandInput = DeleteUserRequest; export interface DeleteUserCommandOutput extends DeleteUserResponse, __MetadataBearer {} @@ -755,8 +750,8 @@ export interface DeviceType { */ DeviceLastAuthenticatedDate?: number; } -export interface ForgetDeviceCommandInput extends ForgetDeviceRequest {} -export interface ForgetDeviceCommandOutput extends __MetadataBearer {} +export type ForgetDeviceCommandInput = ForgetDeviceRequest; +export type ForgetDeviceCommandOutput = __MetadataBearer; /** *

Represents the request to forget the device.

*/ @@ -770,7 +765,7 @@ export interface ForgetDeviceRequest { */ DeviceKey: string | undefined; } -export interface ForgotPasswordCommandInput extends ForgotPasswordRequest {} +export type ForgotPasswordCommandInput = ForgotPasswordRequest; export interface ForgotPasswordCommandOutput extends ForgotPasswordResponse, __MetadataBearer {} @@ -825,9 +820,7 @@ export interface ForgotPasswordRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

Respresents the response from the server regarding the request to reset a password.

@@ -838,8 +831,8 @@ export interface ForgotPasswordResponse { */ CodeDeliveryDetails?: CodeDeliveryDetailsType; } -export interface GetUserAttributeVerificationCodeCommandInput - extends GetUserAttributeVerificationCodeRequest {} +export type GetUserAttributeVerificationCodeCommandInput = + GetUserAttributeVerificationCodeRequest; export interface GetUserAttributeVerificationCodeCommandOutput extends GetUserAttributeVerificationCodeResponse, __MetadataBearer {} @@ -883,9 +876,7 @@ export interface GetUserAttributeVerificationCodeRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

The verification code response returned by the server response to get the user attribute verification code.

@@ -896,7 +887,7 @@ export interface GetUserAttributeVerificationCodeResponse { */ CodeDeliveryDetails?: CodeDeliveryDetailsType; } -export interface GetUserCommandInput extends GetUserRequest {} +export type GetUserCommandInput = GetUserRequest; export interface GetUserCommandOutput extends GetUserResponse, __MetadataBearer {} @@ -937,7 +928,7 @@ export interface GetUserResponse { */ UserMFASettingList?: string[]; } -export interface GlobalSignOutCommandInput extends GlobalSignOutRequest {} +export type GlobalSignOutCommandInput = GlobalSignOutRequest; export interface GlobalSignOutCommandOutput extends GlobalSignOutResponse, __MetadataBearer {} @@ -954,7 +945,7 @@ export interface GlobalSignOutRequest { *

The response to the request to sign out all devices.

*/ export interface GlobalSignOutResponse {} -export interface InitiateAuthCommandInput extends InitiateAuthRequest {} +export type InitiateAuthCommandInput = InitiateAuthRequest; export interface InitiateAuthCommandOutput extends InitiateAuthResponse, __MetadataBearer {} @@ -1021,9 +1012,7 @@ export interface InitiateAuthRequest { * * */ - AuthParameters?: { - [key: string]: string; - }; + AuthParameters?: Record; /** *

A map of custom key-value pairs that you can provide as input for certain custom workflows that this action triggers.

*

You create custom workflows by assigning Lambda functions to user pool triggers. When you use the InitiateAuth API action, Amazon Cognito invokes the Lambda functions that are specified for various @@ -1082,9 +1071,7 @@ export interface InitiateAuthRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; /** *

The app client ID.

*/ @@ -1156,16 +1143,14 @@ export interface InitiateAuthResponse { * the next call (RespondToAuthChallenge).

*

All challenges require USERNAME and SECRET_HASH (if applicable).

*/ - ChallengeParameters?: { - [key: string]: string; - }; + ChallengeParameters?: Record; /** *

The result of the authentication response. This result is only returned if the caller doesn't need to pass another challenge. If the caller does need to pass another challenge before it gets * tokens, ChallengeName, ChallengeParameters, and Session are returned.

*/ AuthenticationResult?: AuthenticationResultType; } -export interface ListDevicesCommandInput extends ListDevicesRequest {} +export type ListDevicesCommandInput = ListDevicesRequest; export interface ListDevicesCommandOutput extends ListDevicesResponse, __MetadataBearer {} @@ -1227,8 +1212,7 @@ export interface NewDeviceMetadataType { */ DeviceGroupKey?: string; } -export interface ResendConfirmationCodeCommandInput - extends ResendConfirmationCodeRequest {} +export type ResendConfirmationCodeCommandInput = ResendConfirmationCodeRequest; export interface ResendConfirmationCodeCommandOutput extends ResendConfirmationCodeResponse, __MetadataBearer {} @@ -1284,9 +1268,7 @@ export interface ResendConfirmationCodeRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

The response from the server when Amazon Cognito makes the request to resend a confirmation code.

@@ -1297,8 +1279,7 @@ export interface ResendConfirmationCodeResponse { */ CodeDeliveryDetails?: CodeDeliveryDetailsType; } -export interface RespondToAuthChallengeCommandInput - extends RespondToAuthChallengeRequest {} +export type RespondToAuthChallengeCommandInput = RespondToAuthChallengeRequest; export interface RespondToAuthChallengeCommandOutput extends RespondToAuthChallengeResponse, __MetadataBearer {} @@ -1363,9 +1344,7 @@ export interface RespondToAuthChallengeRequest { * * */ - ChallengeResponses?: { - [key: string]: string; - }; + ChallengeResponses?: Record; /** *

The Amazon Pinpoint analytics metadata for collecting metrics for RespondToAuthChallenge calls.

*/ @@ -1401,9 +1380,7 @@ export interface RespondToAuthChallengeRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

The response to respond to the authentication challenge.

@@ -1421,9 +1398,7 @@ export interface RespondToAuthChallengeResponse { /** *

The challenge parameters. For more information, see InitiateAuth.

*/ - ChallengeParameters?: { - [key: string]: string; - }; + ChallengeParameters?: Record; /** *

The result returned by the server in response to the request to respond to the authentication challenge.

*/ @@ -1445,8 +1420,7 @@ export interface SMSMfaSettingsType { */ PreferredMfa?: boolean; } -export interface SetUserMFAPreferenceCommandInput - extends SetUserMFAPreferenceRequest {} +export type SetUserMFAPreferenceCommandInput = SetUserMFAPreferenceRequest; export interface SetUserMFAPreferenceCommandOutput extends SetUserMFAPreferenceResponse, __MetadataBearer {} @@ -1465,7 +1439,7 @@ export interface SetUserMFAPreferenceRequest { AccessToken: string | undefined; } export interface SetUserMFAPreferenceResponse {} -export interface SignUpCommandInput extends SignUpRequest {} +export type SignUpCommandInput = SignUpRequest; export interface SignUpCommandOutput extends SignUpResponse, __MetadataBearer {} /** *

Represents the request to register a user.

@@ -1532,9 +1506,7 @@ export interface SignUpRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

The response from the server for a registration request.

@@ -1569,8 +1541,7 @@ export interface SoftwareTokenMfaSettingsType { */ PreferredMfa?: boolean; } -export interface UpdateDeviceStatusCommandInput - extends UpdateDeviceStatusRequest {} +export type UpdateDeviceStatusCommandInput = UpdateDeviceStatusRequest; export interface UpdateDeviceStatusCommandOutput extends UpdateDeviceStatusResponse, __MetadataBearer {} @@ -1595,8 +1566,7 @@ export interface UpdateDeviceStatusRequest { *

The response to the request to update the device status.

*/ export interface UpdateDeviceStatusResponse {} -export interface UpdateUserAttributesCommandInput - extends UpdateUserAttributesRequest {} +export type UpdateUserAttributesCommandInput = UpdateUserAttributesRequest; export interface UpdateUserAttributesCommandOutput extends UpdateUserAttributesResponse, __MetadataBearer {} @@ -1640,9 +1610,7 @@ export interface UpdateUserAttributesRequest { * * */ - ClientMetadata?: { - [key: string]: string; - }; + ClientMetadata?: Record; } /** *

Represents the response from the server for the request to update user attributes.

@@ -1662,8 +1630,7 @@ export interface UserContextDataType { */ EncodedData?: string; } -export interface VerifySoftwareTokenCommandInput - extends VerifySoftwareTokenRequest {} +export type VerifySoftwareTokenCommandInput = VerifySoftwareTokenRequest; export interface VerifySoftwareTokenCommandOutput extends VerifySoftwareTokenResponse, __MetadataBearer {} @@ -1696,8 +1663,7 @@ export interface VerifySoftwareTokenResponse { */ Session?: string; } -export interface VerifyUserAttributeCommandInput - extends VerifyUserAttributeRequest {} +export type VerifyUserAttributeCommandInput = VerifyUserAttributeRequest; export interface VerifyUserAttributeCommandOutput extends VerifyUserAttributeResponse, __MetadataBearer {} @@ -1722,8 +1688,7 @@ export interface VerifyUserAttributeRequest { *

A container representing the response from the server from the request to verify user attributes.

*/ export interface VerifyUserAttributeResponse {} -export interface DeleteUserAttributesCommandInput - extends DeleteUserAttributesRequest {} +export type DeleteUserAttributesCommandInput = DeleteUserAttributesRequest; export interface DeleteUserAttributesCommandOutput extends DeleteUserAttributesResponse, __MetadataBearer {} diff --git a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils.ts b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils.ts index 71ab71bd22c..eca42517fab 100644 --- a/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils.ts +++ b/packages/auth/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthError } from '../../../../../errors/AuthError'; +import { AuthError } from '~/src/errors/AuthError'; export function getRegion(userPoolId?: string): string { const region = userPoolId?.split('_')[0]; @@ -15,6 +15,7 @@ export function getRegion(userPoolId?: string): string { name: 'InvalidUserPoolId', message: 'Invalid user pool id provided.', }); + return region; } @@ -27,5 +28,6 @@ export function getRegionFromIdentityPoolId(identityPoolId?: string): string { 'Make sure a valid identityPoolId is given in the config.', }); } + return identityPoolId.split(':')[0]; } diff --git a/packages/auth/src/providers/cognito/utils/clients/base.ts b/packages/auth/src/providers/cognito/utils/clients/base.ts index 0b71aa38c21..b7713df3f10 100644 --- a/packages/auth/src/providers/cognito/utils/clients/base.ts +++ b/packages/auth/src/providers/cognito/utils/clients/base.ts @@ -9,14 +9,14 @@ import { HttpResponse, Middleware, getDnsSuffix, - unauthenticatedHandler, - parseJsonError, getRetryDecider, jitteredBackoff, + parseJsonError, + unauthenticatedHandler, } from '@aws-amplify/core/internals/aws-client-utils'; import { - getAmplifyUserAgent, AmplifyUrl, + getAmplifyUserAgent, } from '@aws-amplify/core/internals/utils'; import { composeTransferHandler } from '@aws-amplify/core/internals/aws-client-utils/composers'; @@ -30,19 +30,19 @@ const SERVICE_NAME = 'cognito-idp'; */ const endpointResolver = ({ region }: EndpointResolverOptions) => ({ url: new AmplifyUrl( - `https://${SERVICE_NAME}.${region}.${getDnsSuffix(region)}` + `https://${SERVICE_NAME}.${region}.${getDnsSuffix(region)}`, ), }); /** * A Cognito Identity-specific middleware that disables caching for all requests. */ -const disableCacheMiddleware: Middleware = - () => (next, context) => - async function disableCacheMiddleware(request) { - request.headers['cache-control'] = 'no-store'; - return next(request); - }; +const disableCacheMiddleware: Middleware = + () => next => async request => { + request.headers['cache-control'] = 'no-store'; + + return next(request); + }; /** * A Cognito Identity-specific transfer handler that does NOT sign requests, and @@ -82,7 +82,7 @@ export const getSharedHeaders = (operation: string): Headers => ({ export const buildHttpRpcRequest = ( { url }: Endpoint, headers: Headers, - body: any + body: any, ): HttpRequest => ({ headers, url, diff --git a/packages/auth/src/providers/cognito/utils/oauth/completeOAuthSignOut.ts b/packages/auth/src/providers/cognito/utils/oauth/completeOAuthSignOut.ts index 0a904f3e5f9..10b4e7d82b0 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/completeOAuthSignOut.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/completeOAuthSignOut.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { clearCredentials, Hub } from '@aws-amplify/core'; +import { Hub, clearCredentials } from '@aws-amplify/core'; import { AMPLIFY_SYMBOL } from '@aws-amplify/core/internals/utils'; -import { DefaultOAuthStore } from '../../utils/signInWithRedirectStore'; -import { tokenOrchestrator } from '../../tokenProvider'; +import { DefaultOAuthStore } from '~/src/providers/cognito/utils/signInWithRedirectStore'; +import { tokenOrchestrator } from '~/src/providers/cognito/tokenProvider'; export const completeOAuthSignOut = async (store: DefaultOAuthStore) => { await store.clearOAuthData(); diff --git a/packages/auth/src/providers/cognito/utils/oauth/generateCodeVerifier.ts b/packages/auth/src/providers/cognito/utils/oauth/generateCodeVerifier.ts index 58c27468ac9..b6251e95ac8 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/generateCodeVerifier.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/generateCodeVerifier.ts @@ -19,11 +19,11 @@ const CODE_VERIFIER_CHARSET = * following the spec of [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636#section-4.2). */ export const generateCodeVerifier = ( - length: number + length: number, ): { value: string; method: 'S256'; - toCodeChallenge: () => string; + toCodeChallenge(): string; } => { const randomBytes = new Uint8Array(length); getCrypto().getRandomValues(randomBytes); @@ -31,7 +31,7 @@ export const generateCodeVerifier = ( let value = ''; let codeChallenge: string | undefined; - for (let byte of randomBytes) { + for (const byte of randomBytes) { value += CODE_VERIFIER_CHARSET.charAt(byte % CODE_VERIFIER_CHARSET.length); } @@ -43,6 +43,7 @@ export const generateCodeVerifier = ( return codeChallenge; } codeChallenge = generateCodeChallenge(value); + return codeChallenge; }, }; @@ -53,7 +54,7 @@ function generateCodeChallenge(codeVerifier: string): string { awsCryptoHash.update(codeVerifier); const codeChallenge = removePaddingChar( - base64Encoder.convert(awsCryptoHash.digestSync(), { urlSafe: true }) + base64Encoder.convert(awsCryptoHash.digestSync(), { urlSafe: true }), ); return codeChallenge; diff --git a/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.native.ts b/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.native.ts index 5e138350569..6a826cabe5d 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.native.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.native.ts @@ -1,16 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { invalidRedirectException } from '../../../../errors/constants'; +import { invalidRedirectException } from '~/src/errors/constants'; /** @internal */ export function getRedirectUrl(redirects: string[]): string { const redirect = redirects?.find( - redirect => - !redirect.startsWith('http://') && !redirect.startsWith('https://') + item => !item.startsWith('http://') && !item.startsWith('https://'), ); if (!redirect) { throw invalidRedirectException; } + return redirect; } diff --git a/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.ts b/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.ts index 46885a38db1..0fd610a4f89 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.ts @@ -3,7 +3,7 @@ import { invalidOriginException, invalidRedirectException, -} from '../../../../errors/constants'; +} from '~/src/errors/constants'; /** @internal */ export function getRedirectUrl(redirects: string[]): string { @@ -23,7 +23,7 @@ export function getRedirectUrl(redirects: string[]): string { // origin + pathname => https://example.com/app const isSameOriginAndPathName = (redirect: string) => redirect.startsWith( - String(window.location.origin + window.location.pathname ?? '/') + String(window.location.origin + window.location.pathname ?? '/'), ); // domain => outlook.live.com, github.com const isTheSameDomain = (redirect: string) => diff --git a/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.native.ts b/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.native.ts index 3cdb0315a28..bc82bf1d514 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.native.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.native.ts @@ -2,21 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 import { CognitoUserPoolConfig } from '@aws-amplify/core'; -import { OpenAuthSessionResult } from '../../../../utils/types'; -import { DefaultOAuthStore } from '../../utils/signInWithRedirectStore'; +import { OpenAuthSessionResult } from '~/src/utils/types'; +import { DefaultOAuthStore } from '~/src/providers/cognito/utils/signInWithRedirectStore'; + import { completeOAuthSignOut } from './completeOAuthSignOut'; import { oAuthSignOutRedirect } from './oAuthSignOutRedirect'; export const handleOAuthSignOut = async ( cognitoConfig: CognitoUserPoolConfig, - store: DefaultOAuthStore + store: DefaultOAuthStore, ): Promise => { const { isOAuthSignIn, preferPrivateSession } = await store.loadOAuthSignIn(); if (isOAuthSignIn) { const result = await oAuthSignOutRedirect( cognitoConfig, - preferPrivateSession + preferPrivateSession, ); // If this was a private session, clear data and tokens regardless of what happened with logout // endpoint. Otherwise, only do so if the logout endpoint was succesfully visited. @@ -25,6 +26,7 @@ export const handleOAuthSignOut = async ( if (shouldCompleteSignOut) { await completeOAuthSignOut(store); } + return result; } diff --git a/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.ts b/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.ts index a583af266fb..aad2c1dfb81 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.ts @@ -2,14 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { CognitoUserPoolConfig } from '@aws-amplify/core'; -import { OpenAuthSessionResult } from '../../../../utils/types'; -import { DefaultOAuthStore } from '../../utils/signInWithRedirectStore'; +import { OpenAuthSessionResult } from '~/src/utils/types'; +import { DefaultOAuthStore } from '~/src/providers/cognito/utils/signInWithRedirectStore'; + import { completeOAuthSignOut } from './completeOAuthSignOut'; import { oAuthSignOutRedirect } from './oAuthSignOutRedirect'; export const handleOAuthSignOut = async ( cognitoConfig: CognitoUserPoolConfig, - store: DefaultOAuthStore + store: DefaultOAuthStore, ): Promise => { const { isOAuthSignIn } = await store.loadOAuthSignIn(); diff --git a/packages/auth/src/providers/cognito/utils/oauth/oAuthSignOutRedirect.ts b/packages/auth/src/providers/cognito/utils/oauth/oAuthSignOutRedirect.ts index 2cebfefc521..6c498ed89d1 100644 --- a/packages/auth/src/providers/cognito/utils/oauth/oAuthSignOutRedirect.ts +++ b/packages/auth/src/providers/cognito/utils/oauth/oAuthSignOutRedirect.ts @@ -3,13 +3,14 @@ import { CognitoUserPoolConfig } from '@aws-amplify/core'; import { assertOAuthConfig } from '@aws-amplify/core/internals/utils'; -import { openAuthSession } from '../../../../utils'; -import { OpenAuthSessionResult } from '../../../../utils/types'; +import { openAuthSession } from '~/src/utils'; +import { OpenAuthSessionResult } from '~/src/utils/types'; + import { getRedirectUrl } from './getRedirectUrl'; export const oAuthSignOutRedirect = async ( authConfig: CognitoUserPoolConfig, - preferPrivateSession: boolean = false + preferPrivateSession = false, ): Promise => { assertOAuthConfig(authConfig); const { loginWith, userPoolClientId } = authConfig; @@ -25,6 +26,6 @@ export const oAuthSignOutRedirect = async ( return openAuthSession( oAuthLogoutEndpoint, redirectSignOut, - preferPrivateSession + preferPrivateSession, ); }; diff --git a/packages/auth/src/providers/cognito/utils/refreshAuthTokens.ts b/packages/auth/src/providers/cognito/utils/refreshAuthTokens.ts index f74c8683fd6..90e423bd1f8 100644 --- a/packages/auth/src/providers/cognito/utils/refreshAuthTokens.ts +++ b/packages/auth/src/providers/cognito/utils/refreshAuthTokens.ts @@ -1,16 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { CognitoAuthTokens, TokenRefresher } from '../tokenProvider/types'; +import { + CognitoAuthTokens, + TokenRefresher, +} from '~/src/providers/cognito/tokenProvider/types'; import { AuthConfig } from '@aws-amplify/core'; import { assertTokenProviderConfig, decodeJWT, } from '@aws-amplify/core/internals/utils'; -import { initiateAuth } from '../utils/clients/CognitoIdentityProvider'; -import { getRegion } from '../utils/clients/CognitoIdentityProvider/utils'; -import { assertAuthTokensWithRefreshToken } from '../utils/types'; -import { AuthError } from '../../../errors/AuthError'; +import { initiateAuth } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider'; +import { getRegion } from '~/src/providers/cognito/utils/clients/CognitoIdentityProvider/utils'; +import { assertAuthTokensWithRefreshToken } from '~/src/providers/cognito/utils/types'; +import { AuthError } from '~/src/errors/AuthError'; + import { getUserContextData } from './userContextData'; export const refreshAuthTokens: TokenRefresher = async ({ @@ -31,7 +35,7 @@ export const refreshAuthTokens: TokenRefresher = async ({ REFRESH_TOKEN: refreshTokenString, }; if (tokens.deviceMetadata?.deviceKey) { - AuthParameters['DEVICE_KEY'] = tokens.deviceMetadata.deviceKey; + AuthParameters.DEVICE_KEY = tokens.deviceMetadata.deviceKey; } const UserContextData = getUserContextData({ @@ -47,14 +51,14 @@ export const refreshAuthTokens: TokenRefresher = async ({ AuthFlow: 'REFRESH_TOKEN_AUTH', AuthParameters, UserContextData, - } + }, ); const accessToken = decodeJWT(AuthenticationResult?.AccessToken ?? ''); const idToken = AuthenticationResult?.IdToken ? decodeJWT(AuthenticationResult.IdToken) : undefined; - const iat = accessToken.payload.iat; + const { iat } = accessToken.payload; // This should never happen. If it does, it's a bug from the service. if (!iat) { throw new AuthError({ diff --git a/packages/auth/src/providers/cognito/utils/signInHelpers.ts b/packages/auth/src/providers/cognito/utils/signInHelpers.ts index 372ed25e700..22184d11c05 100644 --- a/packages/auth/src/providers/cognito/utils/signInHelpers.ts +++ b/packages/auth/src/providers/cognito/utils/signInHelpers.ts @@ -3,44 +3,46 @@ import { Amplify, CognitoUserPoolConfig } from '@aws-amplify/core'; import { + AmplifyUrl, AuthAction, assertTokenProviderConfig, base64Encoder, - AmplifyUrl, } from '@aws-amplify/core/internals/utils'; -import { AuthenticationHelper } from './srp/AuthenticationHelper'; -import { BigInteger } from './srp/BigInteger'; import { - getAuthenticationHelper, - getBytesFromHex, - getNowString, - getSignatureString, -} from './srp'; - -import { ClientMetadata, ConfirmSignInOptions } from '../types'; + ClientMetadata, + ConfirmSignInOptions, +} from '~/src/providers/cognito/types'; import { AuthAdditionalInfo, - AuthSignInOutput, AuthDeliveryMedium, -} from '../../../types'; -import { AuthError } from '../../../errors/AuthError'; -import { InitiateAuthException } from '../types/errors'; + AuthSignInOutput, +} from '~/src/types'; +import { AuthError } from '~/src/errors/AuthError'; +import { InitiateAuthException } from '~/src/providers/cognito/types/errors'; import { AWSAuthUser, - AuthUserAttributes, AuthMFAType, AuthTOTPSetupDetails, -} from '../../../types/models'; -import { AuthErrorCodes } from '../../../common/AuthErrorStrings'; -import { AuthValidationErrorCode } from '../../../errors/types/validation'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; + AuthUserAttributes, +} from '~/src/types/models'; +import { AuthErrorCodes } from '~/src/common/AuthErrorStrings'; +import { AuthValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { USER_ALREADY_AUTHENTICATED_EXCEPTION } from '~/src/errors/constants'; +import { getCurrentUser } from '~/src/providers/cognito/apis/getCurrentUser'; +import { + AuthTokenOrchestrator, + DeviceMetadata, +} from '~/src/providers/cognito/tokenProvider/types'; +import { getAuthUserAgentValue } from '~/src/utils'; + import { signInStore } from './signInStore'; import { + associateSoftwareToken, + confirmDevice, initiateAuth, respondToAuthChallenge, verifySoftwareToken, - associateSoftwareToken, - confirmDevice, } from './clients/CognitoIdentityProvider'; import { ChallengeName, @@ -53,16 +55,20 @@ import { RespondToAuthChallengeCommandOutput, } from './clients/CognitoIdentityProvider/types'; import { getRegion } from './clients/CognitoIdentityProvider/utils'; -import { USER_ALREADY_AUTHENTICATED_EXCEPTION } from '../../../errors/constants'; -import { getCurrentUser } from '../apis/getCurrentUser'; -import { AuthTokenOrchestrator, DeviceMetadata } from '../tokenProvider/types'; import { assertDeviceMetadata } from './types'; -import { getAuthUserAgentValue } from '../../../utils'; +import { + getAuthenticationHelper, + getBytesFromHex, + getNowString, + getSignatureString, +} from './srp'; +import { BigInteger } from './srp/BigInteger'; +import { AuthenticationHelper } from './srp/AuthenticationHelper'; import { getUserContextData } from './userContextData'; const USER_ATTRIBUTES = 'userAttributes.'; -type HandleAuthChallengeRequest = { +interface HandleAuthChallengeRequest { challengeResponse: string; username: string; clientMetadata?: ClientMetadata; @@ -70,15 +76,15 @@ type HandleAuthChallengeRequest = { deviceName?: string; requiredAttributes?: AuthUserAttributes; config: CognitoUserPoolConfig; -}; +} -type HandleDeviceSRPInput = { +interface HandleDeviceSRPInput { username: string; config: CognitoUserPoolConfig; clientMetadata: ClientMetadata | undefined; session: string | undefined; tokenOrchestrator?: AuthTokenOrchestrator; -}; +} export async function handleCustomChallenge({ challengeResponse, @@ -98,7 +104,7 @@ export async function handleCustomChallenge({ const deviceMetadata = await tokenOrchestrator?.getDeviceMetadata(username); if (deviceMetadata && deviceMetadata.deviceKey) { - challengeResponses['DEVICE_KEY'] = deviceMetadata.deviceKey; + challengeResponses.DEVICE_KEY = deviceMetadata.deviceKey; } const UserContextData = getUserContextData({ @@ -121,7 +127,7 @@ export async function handleCustomChallenge({ region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn), }, - jsonReq + jsonReq, ); if (response.ChallengeName === 'DEVICE_SRP_AUTH') { @@ -159,7 +165,7 @@ export async function handleMFASetupChallenge({ UserCode: challengeResponse, Session: session, FriendlyDeviceName: deviceName, - } + }, ); signInStore.dispatch({ @@ -174,6 +180,7 @@ export async function handleMFASetupChallenge({ ClientMetadata: clientMetadata, ClientId: userPoolClientId, }; + return respondToAuthChallenge({ region: getRegion(userPoolId) }, jsonReq); } @@ -187,7 +194,7 @@ export async function handleSelectMFATypeChallenge({ const { userPoolId, userPoolClientId } = config; assertValidationError( challengeResponse === 'TOTP' || challengeResponse === 'SMS', - AuthValidationErrorCode.IncorrectMFAMethod + AuthValidationErrorCode.IncorrectMFAMethod, ); const challengeResponses = { @@ -215,7 +222,7 @@ export async function handleSelectMFATypeChallenge({ region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn), }, - jsonReq + jsonReq, ); } @@ -250,7 +257,7 @@ export async function handleSMSMFAChallenge({ region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn), }, - jsonReq + jsonReq, ); } export async function handleSoftwareTokenMFAChallenge({ @@ -280,12 +287,13 @@ export async function handleSoftwareTokenMFAChallenge({ ClientId: userPoolClientId, UserContextData, }; + return respondToAuthChallenge( { region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn), }, - jsonReq + jsonReq, ); } export async function handleCompleteNewPasswordChallenge({ @@ -323,7 +331,7 @@ export async function handleCompleteNewPasswordChallenge({ region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.ConfirmSignIn), }, - jsonReq + jsonReq, ); } @@ -332,7 +340,7 @@ export async function handleUserPasswordAuthFlow( password: string, clientMetadata: ClientMetadata | undefined, config: CognitoUserPoolConfig, - tokenOrchestrator: AuthTokenOrchestrator + tokenOrchestrator: AuthTokenOrchestrator, ): Promise { const { userPoolClientId, userPoolId } = config; const authParameters: Record = { @@ -342,7 +350,7 @@ export async function handleUserPasswordAuthFlow( const deviceMetadata = await tokenOrchestrator.getDeviceMetadata(username); if (deviceMetadata && deviceMetadata.deviceKey) { - authParameters['DEVICE_KEY'] = deviceMetadata.deviceKey; + authParameters.DEVICE_KEY = deviceMetadata.deviceKey; } const UserContextData = getUserContextData({ @@ -364,7 +372,7 @@ export async function handleUserPasswordAuthFlow( region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.SignIn), }, - jsonReq + jsonReq, ); const activeUsername = @@ -382,6 +390,7 @@ export async function handleUserPasswordAuthFlow( session: response.Session, tokenOrchestrator, }); + return response; } @@ -390,7 +399,7 @@ export async function handleUserSRPAuthFlow( password: string, clientMetadata: ClientMetadata | undefined, config: CognitoUserPoolConfig, - tokenOrchestrator: AuthTokenOrchestrator + tokenOrchestrator: AuthTokenOrchestrator, ): Promise { const { userPoolId, userPoolClientId } = config; const userPoolName = userPoolId?.split('_')[1] || ''; @@ -420,11 +429,12 @@ export async function handleUserSRPAuthFlow( region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.SignIn), }, - jsonReq + jsonReq, ); const { ChallengeParameters: challengeParameters, Session: session } = resp; const activeUsername = challengeParameters?.USERNAME ?? username; setActiveSignInUsername(activeUsername); + return retryOnResourceNotFoundException( handlePasswordVerifierChallenge, [ @@ -437,7 +447,7 @@ export async function handleUserSRPAuthFlow( tokenOrchestrator, ], activeUsername, - tokenOrchestrator + tokenOrchestrator, ); } @@ -445,17 +455,16 @@ export async function handleCustomAuthFlowWithoutSRP( username: string, clientMetadata: ClientMetadata | undefined, config: CognitoUserPoolConfig, - tokenOrchestrator: AuthTokenOrchestrator + tokenOrchestrator: AuthTokenOrchestrator, ): Promise { const { userPoolClientId, userPoolId } = config; - const { dispatch } = signInStore; const authParameters: Record = { USERNAME: username, }; const deviceMetadata = await tokenOrchestrator.getDeviceMetadata(username); if (deviceMetadata && deviceMetadata.deviceKey) { - authParameters['DEVICE_KEY'] = deviceMetadata.deviceKey; + authParameters.DEVICE_KEY = deviceMetadata.deviceKey; } const UserContextData = getUserContextData({ @@ -477,7 +486,7 @@ export async function handleCustomAuthFlowWithoutSRP( region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.SignIn), }, - jsonReq + jsonReq, ); const activeUsername = response.ChallengeParameters?.USERNAME ?? username; setActiveSignInUsername(activeUsername); @@ -489,6 +498,7 @@ export async function handleCustomAuthFlowWithoutSRP( session: response.Session, tokenOrchestrator, }); + return response; } @@ -497,7 +507,7 @@ export async function handleCustomSRPAuthFlow( password: string, clientMetadata: ClientMetadata | undefined, config: CognitoUserPoolConfig, - tokenOrchestrator: AuthTokenOrchestrator + tokenOrchestrator: AuthTokenOrchestrator, ) { assertTokenProviderConfig(config); const { userPoolId, userPoolClientId } = config; @@ -531,7 +541,7 @@ export async function handleCustomSRPAuthFlow( region: getRegion(userPoolId), userAgentValue: getAuthUserAgentValue(AuthAction.SignIn), }, - jsonReq + jsonReq, ); const activeUsername = challengeParameters?.USERNAME ?? username; setActiveSignInUsername(activeUsername); @@ -548,7 +558,7 @@ export async function handleCustomSRPAuthFlow( tokenOrchestrator, ], activeUsername, - tokenOrchestrator + tokenOrchestrator, ); } @@ -559,12 +569,12 @@ async function handleDeviceSRPAuth({ session, tokenOrchestrator, }: HandleDeviceSRPInput): Promise { - const userPoolId = config.userPoolId; + const { userPoolId } = config; const clientId = config.userPoolClientId; const deviceMetadata = await tokenOrchestrator?.getDeviceMetadata(username); assertDeviceMetadata(deviceMetadata); const authenticationHelper = await getAuthenticationHelper( - deviceMetadata.deviceGroupKey + deviceMetadata.deviceGroupKey, ); const challengeResponses: Record = { USERNAME: username, @@ -579,19 +589,20 @@ async function handleDeviceSRPAuth({ ClientMetadata: clientMetadata, Session: session, }; - const { ChallengeParameters, Session } = await respondToAuthChallenge( - { region: getRegion(userPoolId) }, - jsonReqResponseChallenge - ); + const { ChallengeParameters: challengeParameters, Session } = + await respondToAuthChallenge( + { region: getRegion(userPoolId) }, + jsonReqResponseChallenge, + ); return handleDevicePasswordVerifier( username, - ChallengeParameters as ChallengeParameters, + challengeParameters as ChallengeParameters, clientMetadata, Session, authenticationHelper, config, - tokenOrchestrator + tokenOrchestrator, ); } @@ -602,15 +613,15 @@ async function handleDevicePasswordVerifier( session: string | undefined, authenticationHelper: AuthenticationHelper, { userPoolId, userPoolClientId }: CognitoUserPoolConfig, - tokenOrchestrator?: AuthTokenOrchestrator + tokenOrchestrator?: AuthTokenOrchestrator, ): Promise { const deviceMetadata = await tokenOrchestrator?.getDeviceMetadata(username); assertDeviceMetadata(deviceMetadata); const serverBValue = new BigInteger(challengeParameters?.SRP_B, 16); const salt = new BigInteger(challengeParameters?.SALT, 16); - const deviceKey = deviceMetadata.deviceKey; - const deviceGroupKey = deviceMetadata.deviceGroupKey; + const { deviceKey } = deviceMetadata; + const { deviceGroupKey } = deviceMetadata; const hkdf = await authenticationHelper.getPasswordAuthenticationKey({ username: deviceMetadata.deviceKey, password: deviceMetadata.randomPassword, @@ -631,7 +642,7 @@ async function handleDevicePasswordVerifier( hkdf, }), DEVICE_KEY: deviceKey, - } as { [key: string]: string }; + } as Record; const UserContextData = getUserContextData({ username, @@ -650,7 +661,7 @@ async function handleDevicePasswordVerifier( return respondToAuthChallenge( { region: getRegion(userPoolId) }, - jsonReqResponseChallenge + jsonReqResponseChallenge, ); } @@ -661,7 +672,7 @@ export async function handlePasswordVerifierChallenge( session: string | undefined, authenticationHelper: AuthenticationHelper, config: CognitoUserPoolConfig, - tokenOrchestrator: AuthTokenOrchestrator + tokenOrchestrator: AuthTokenOrchestrator, ): Promise { const { userPoolId, userPoolClientId } = config; const userPoolName = userPoolId?.split('_')[1] || ''; @@ -693,11 +704,11 @@ export async function handlePasswordVerifierChallenge( dateNow, hkdf, }), - } as { [key: string]: string }; + } as Record; const deviceMetadata = await tokenOrchestrator.getDeviceMetadata(username); if (deviceMetadata && deviceMetadata.deviceKey) { - challengeResponses['DEVICE_KEY'] = deviceMetadata.deviceKey; + challengeResponses.DEVICE_KEY = deviceMetadata.deviceKey; } const UserContextData = getUserContextData({ @@ -717,7 +728,7 @@ export async function handlePasswordVerifierChallenge( const response = await respondToAuthChallenge( { region: getRegion(userPoolId) }, - jsonReqResponseChallenge + jsonReqResponseChallenge, ); if (response.ChallengeName === 'DEVICE_SRP_AUTH') @@ -728,6 +739,7 @@ export async function handlePasswordVerifierChallenge( session: response.Session, tokenOrchestrator, }); + return response; } @@ -748,21 +760,21 @@ export async function getSignInResult(params: { additionalInfo: challengeParameters as AuthAdditionalInfo, }, }; - case 'MFA_SETUP': + case 'MFA_SETUP': { const { signInSession, username } = signInStore.getState(); if (!isMFATypeEnabled(challengeParameters, 'TOTP')) throw new AuthError({ name: AuthErrorCodes.SignInException, message: `Cannot initiate MFA setup from available types: ${getMFATypes( - parseMFATypes(challengeParameters.MFAS_CAN_SETUP) + parseMFATypes(challengeParameters.MFAS_CAN_SETUP), )}`, }); const { Session, SecretCode: secretCode } = await associateSoftwareToken( { region: getRegion(authConfig.userPoolId) }, { Session: signInSession, - } + }, ); signInStore.dispatch({ type: 'SET_SIGN_IN_SESSION', @@ -776,13 +788,14 @@ export async function getSignInResult(params: { totpSetupDetails: getTOTPSetupDetails(secretCode!, username), }, }; + } case 'NEW_PASSWORD_REQUIRED': return { isSignedIn: false, nextStep: { signInStep: 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED', missingAttributes: parseAttributes( - challengeParameters.requiredAttributes + challengeParameters.requiredAttributes, ), }, }; @@ -792,7 +805,7 @@ export async function getSignInResult(params: { nextStep: { signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION', allowedMFATypes: getMFATypes( - parseMFATypes(challengeParameters.MFAS_CAN_CHOOSE) + parseMFATypes(challengeParameters.MFAS_CAN_CHOOSE), ), }, }; @@ -835,7 +848,7 @@ export async function getSignInResult(params: { export function getTOTPSetupDetails( secretCode: string, - username?: string + username?: string, ): AuthTOTPSetupDetails { return { sharedSecret: secretCode, @@ -850,7 +863,7 @@ export function getTOTPSetupDetails( } export function getSignInResultFromError( - errorName: string + errorName: string, ): AuthSignInOutput | undefined { if (errorName === InitiateAuthException.PasswordResetRequiredException) { return { @@ -867,15 +880,15 @@ export function getSignInResultFromError( export function parseAttributes(attributes: string | undefined): string[] { if (!attributes) return []; - const parsedAttributes = (JSON.parse(attributes) as Array).map(att => - att.includes(USER_ATTRIBUTES) ? att.replace(USER_ATTRIBUTES, '') : att + const parsedAttributes = (JSON.parse(attributes) as string[]).map(att => + att.includes(USER_ATTRIBUTES) ? att.replace(USER_ATTRIBUTES, '') : att, ); return parsedAttributes; } export function createAttributes( - attributes?: AuthUserAttributes + attributes?: AuthUserAttributes, ): Record { if (!attributes) return {}; @@ -884,6 +897,7 @@ export function createAttributes( Object.entries(attributes).forEach(([key, value]) => { if (value) newAttributes[`${USER_ATTRIBUTES}${key}`] = value; }); + return newAttributes; } @@ -895,7 +909,7 @@ export async function handleChallengeName( config: CognitoUserPoolConfig, tokenOrchestrator: AuthTokenOrchestrator, clientMetadata?: ClientMetadata, - options?: ConfirmSignInOptions + options?: ConfirmSignInOptions, ): Promise { const userAttributes = options?.userAttributes; const deviceName = options?.friendlyDeviceName; @@ -949,7 +963,7 @@ export async function handleChallengeName( }, ], username, - tokenOrchestrator + tokenOrchestrator, ); case 'SOFTWARE_TOKEN_MFA': return handleSoftwareTokenMFAChallenge({ @@ -983,20 +997,23 @@ export function getMFAType(type?: string): AuthMFAType | undefined { export function getMFATypes(types?: string[]): AuthMFAType[] | undefined { if (!types) return undefined; + return types.map(getMFAType).filter(Boolean) as AuthMFAType[]; } export function parseMFATypes(mfa?: string): CognitoMFAType[] { if (!mfa) return []; + return JSON.parse(mfa) as CognitoMFAType[]; } export function isMFATypeEnabled( challengeParams: ChallengeParameters, - mfaType: AuthMFAType + mfaType: AuthMFAType, ): boolean { const { MFAS_CAN_SETUP } = challengeParams; const mfaTypes = getMFATypes(parseMFATypes(MFAS_CAN_SETUP)); if (!mfaTypes) return false; + return mfaTypes.includes(mfaType); } @@ -1027,7 +1044,7 @@ export async function assertUserNotAuthenticated() { export async function getNewDeviceMetatada( userPoolId: string, newDeviceMetadata?: NewDeviceMetadataType, - accessToken?: string + accessToken?: string, ): Promise { if (!newDeviceMetadata) return undefined; const userPoolName = userPoolId.split('_')[1] || ''; @@ -1038,7 +1055,7 @@ export async function getNewDeviceMetatada( try { await authenticationHelper.generateHashDevice( deviceGroupKey ?? '', - deviceKey ?? '' + deviceKey ?? '', ); } catch (errGenHash) { // TODO: log error here @@ -1047,10 +1064,10 @@ export async function getNewDeviceMetatada( const deviceSecretVerifierConfig = { Salt: base64Encoder.convert( - getBytesFromHex(authenticationHelper.getSaltToHashDevices()) + getBytesFromHex(authenticationHelper.getSaltToHashDevices()), ), PasswordVerifier: base64Encoder.convert( - getBytesFromHex(authenticationHelper.getVerifierDevices()) + getBytesFromHex(authenticationHelper.getVerifierDevices()), ), }; const randomPassword = authenticationHelper.getRandomPassword(); @@ -1062,7 +1079,7 @@ export async function getNewDeviceMetatada( AccessToken: accessToken, DeviceKey: newDeviceMetadata?.DeviceKey, DeviceSecretVerifierConfig: deviceSecretVerifierConfig, - } + }, ); return { @@ -1087,7 +1104,7 @@ export async function retryOnResourceNotFoundException< func: F, args: Parameters, username: string, - tokenOrchestrator: AuthTokenOrchestrator + tokenOrchestrator: AuthTokenOrchestrator, ): Promise> { try { return await func(...args); @@ -1099,7 +1116,7 @@ export async function retryOnResourceNotFoundException< ) { await tokenOrchestrator.clearDeviceMetadata(username); - return await func(...args); + return func(...args); } throw error; } @@ -1113,5 +1130,6 @@ export function setActiveSignInUsername(username: string) { export function getActiveSignInUsername(username: string): string { const state = signInStore.getState(); + return state.username ?? username; } diff --git a/packages/auth/src/providers/cognito/utils/signInStore.ts b/packages/auth/src/providers/cognito/utils/signInStore.ts index b50774b1c71..3fd00e875f4 100644 --- a/packages/auth/src/providers/cognito/utils/signInStore.ts +++ b/packages/auth/src/providers/cognito/utils/signInStore.ts @@ -1,16 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { CognitoAuthSignInDetails } from '../types'; +import { CognitoAuthSignInDetails } from '~/src/providers/cognito/types'; + import { ChallengeName } from './clients/CognitoIdentityProvider/types'; // TODO: replace all of this implementation with state machines -type SignInState = { +interface SignInState { username?: string; challengeName?: ChallengeName; signInSession?: string; signInDetails?: CognitoAuthSignInDetails; -}; +} type SignInAction = | { type: 'SET_INITIAL_STATE' } @@ -20,7 +21,7 @@ type SignInAction = | { type: 'SET_SIGN_IN_SESSION'; value?: string }; type Store = (reducer: Reducer) => { - getState: () => ReturnType>; + getState(): ReturnType>; dispatch(action: Action): void; }; diff --git a/packages/auth/src/providers/cognito/utils/signInWithRedirectStore.ts b/packages/auth/src/providers/cognito/utils/signInWithRedirectStore.ts index 79b5df9acf7..2d6caa0cd53 100644 --- a/packages/auth/src/providers/cognito/utils/signInWithRedirectStore.ts +++ b/packages/auth/src/providers/cognito/utils/signInWithRedirectStore.ts @@ -5,10 +5,11 @@ import { CognitoUserPoolConfig, KeyValueStorageInterface, } from '@aws-amplify/core'; -import { OAuthStorageKeys, OAuthStore } from './types'; -import { getAuthStorageKeys } from '../tokenProvider/TokenStore'; +import { getAuthStorageKeys } from '~/src/providers/cognito/tokenProvider/TokenStore'; import { assertTokenProviderConfig } from '@aws-amplify/core/internals/utils'; +import { OAuthStorageKeys, OAuthStore } from './types'; + const V5_HOSTED_UI_KEY = 'amplify-signin-with-hostedUI'; const name = 'CognitoIdentityServiceProvider'; @@ -19,12 +20,13 @@ export class DefaultOAuthStore implements OAuthStore { constructor(keyValueStorage: KeyValueStorageInterface) { this.keyValueStorage = keyValueStorage; } + async clearOAuthInflightData(): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); await Promise.all([ this.keyValueStorage.removeItem(authKeys.inflightOAuth), @@ -32,52 +34,58 @@ export class DefaultOAuthStore implements OAuthStore { this.keyValueStorage.removeItem(authKeys.oauthState), ]); } + async clearOAuthData(): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); await this.clearOAuthInflightData(); await this.keyValueStorage.removeItem(V5_HOSTED_UI_KEY); // remove in case a customer migrated an App from v5 to v6 + return this.keyValueStorage.removeItem(authKeys.oauthSignIn); } + loadOAuthState(): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); return this.keyValueStorage.getItem(authKeys.oauthState); } + storeOAuthState(state: string): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); return this.keyValueStorage.setItem(authKeys.oauthState, state); } + loadPKCE(): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); return this.keyValueStorage.getItem(authKeys.oauthPKCE); } + storePKCE(pkce: string): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); return this.keyValueStorage.setItem(authKeys.oauthPKCE, pkce); @@ -86,12 +94,13 @@ export class DefaultOAuthStore implements OAuthStore { setAuthConfig(authConfigParam: CognitoUserPoolConfig): void { this.cognitoConfig = authConfigParam; } + async loadOAuthInFlight(): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); return ( @@ -103,13 +112,10 @@ export class DefaultOAuthStore implements OAuthStore { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); - return await this.keyValueStorage.setItem( - authKeys.inflightOAuth, - `${inflight}` - ); + await this.keyValueStorage.setItem(authKeys.inflightOAuth, `${inflight}`); } async loadOAuthSignIn(): Promise<{ @@ -120,7 +126,7 @@ export class DefaultOAuthStore implements OAuthStore { const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); const isLegacyHostedUISignIn = @@ -139,18 +145,18 @@ export class DefaultOAuthStore implements OAuthStore { async storeOAuthSignIn( oauthSignIn: boolean, - preferPrivateSession: boolean = false + preferPrivateSession = false, ): Promise { assertTokenProviderConfig(this.cognitoConfig); const authKeys = createKeysForAuthStorage( name, - this.cognitoConfig.userPoolClientId + this.cognitoConfig.userPoolClientId, ); - return await this.keyValueStorage.setItem( + await this.keyValueStorage.setItem( authKeys.oauthSignIn, - `${oauthSignIn},${preferPrivateSession}` + `${oauthSignIn},${preferPrivateSession}`, ); } } diff --git a/packages/auth/src/providers/cognito/utils/signUpHelpers.ts b/packages/auth/src/providers/cognito/utils/signUpHelpers.ts index 55ecd5bb228..f2667fd1144 100644 --- a/packages/auth/src/providers/cognito/utils/signUpHelpers.ts +++ b/packages/auth/src/providers/cognito/utils/signUpHelpers.ts @@ -2,14 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { HubInternal } from '@aws-amplify/core/internals/utils'; -import { signIn } from '../apis/signIn'; -import { SignInInput, SignInOutput } from '../types'; -import { AutoSignInEventData } from '../types/models'; -import { AutoSignInCallback } from '../../../types/models'; -import { AuthError } from '../../../errors/AuthError'; +import { signIn } from '~/src/providers/cognito/apis/signIn'; +import { SignInInput, SignInOutput } from '~/src/providers/cognito/types'; +import { AutoSignInEventData } from '~/src/providers/cognito/types/models'; +import { AutoSignInCallback } from '~/src/types/models'; +import { AuthError } from '~/src/errors/AuthError'; +import { + resetAutoSignIn, + setAutoSignIn, +} from '~/src/providers/cognito/apis/autoSignIn'; +import { AUTO_SIGN_IN_EXCEPTION } from '~/src/errors/constants'; + import { SignUpCommandOutput } from './clients/CognitoIdentityProvider/types'; -import { resetAutoSignIn, setAutoSignIn } from '../apis/autoSignIn'; -import { AUTO_SIGN_IN_EXCEPTION } from '../../../errors/constants'; const MAX_AUTOSIGNIN_POLLING_MS = 3 * 60 * 1000; @@ -29,7 +33,7 @@ export function handleCodeAutoSignIn(signInInput: SignInInput) { } } } - } + }, ); // This will stop the listener if confirmSignUp is not resolved. @@ -48,11 +52,10 @@ export function handleCodeAutoSignIn(signInInput: SignInInput) { // https://github.com/facebook/react/issues/24502 // https://legacy.reactjs.org/docs/strict-mode.html#ensuring-reusable-state type TimeOutOutput = ReturnType; -function debounce any>(fun: F, delay: number) { +const debounce = any>(fun: F, delay: number) => { let timer: TimeOutOutput | undefined; - return function ( - args: F extends (...args: infer A) => any ? A : never - ): void { + + return (args: F extends (...args: infer A) => any ? A : never): void => { if (!timer) { fun(...args); } @@ -61,12 +64,12 @@ function debounce any>(fun: F, delay: number) { timer = undefined; }, delay); }; -} +}; function handleAutoSignInWithLink( signInInput: SignInInput, - resolve: Function, - reject: Function + resolve: (output: SignInOutput) => void, + reject: (error: unknown) => void, ) { const start = Date.now(); const autoSignInPollingIntervalId = setInterval(async () => { @@ -81,10 +84,9 @@ function handleAutoSignInWithLink( message: 'The account was not confirmed on time.', recoverySuggestion: 'Try to verify your account by clicking the link sent your email or phone and then login manually.', - }) + }), ); resetAutoSignIn(); - return; } else { try { const signInOutput = await signIn(signInInput); @@ -93,7 +95,6 @@ function handleAutoSignInWithLink( clearInterval(autoSignInPollingIntervalId); setAutoSignInStarted(false); resetAutoSignIn(); - return; } } catch (error) { clearInterval(autoSignInPollingIntervalId); @@ -107,10 +108,10 @@ function handleAutoSignInWithLink( const debouncedAutoSignInWithLink = debounce(handleAutoSignInWithLink, 300); const debouncedAutoSignWithCodeOrUserConfirmed = debounce( handleAutoSignInWithCodeOrUserConfirmed, - 300 + 300, ); -let autoSignInStarted: boolean = false; +let autoSignInStarted = false; let usernameUsedForAutoSignIn: string | undefined; @@ -136,18 +137,18 @@ export function isSignUpComplete(output: SignUpCommandOutput): boolean { } export function autoSignInWhenUserIsConfirmedWithLink( - signInInput: SignInInput + signInInput: SignInInput, ): AutoSignInCallback { return async () => { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { debouncedAutoSignInWithLink([signInInput, resolve, reject]); }); }; } async function handleAutoSignInWithCodeOrUserConfirmed( signInInput: SignInInput, - resolve: Function, - reject: Function + resolve: (output: SignInOutput) => void, + reject: (error: unknown) => void, ) { try { const output = await signIn(signInInput); @@ -161,7 +162,7 @@ async function handleAutoSignInWithCodeOrUserConfirmed( function autoSignInWithCode(signInInput: SignInInput): AutoSignInCallback { return async () => { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { debouncedAutoSignWithCodeOrUserConfirmed([signInInput, resolve, reject]); }); }; diff --git a/packages/auth/src/providers/cognito/utils/srp/AuthenticationHelper/AuthenticationHelper.ts b/packages/auth/src/providers/cognito/utils/srp/AuthenticationHelper/AuthenticationHelper.ts index 49a3cf4142d..a4154195f96 100644 --- a/packages/auth/src/providers/cognito/utils/srp/AuthenticationHelper/AuthenticationHelper.ts +++ b/packages/auth/src/providers/cognito/utils/srp/AuthenticationHelper/AuthenticationHelper.ts @@ -1,18 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthError } from '../../../../../errors/AuthError'; -import { textEncoder } from '../../textEncoder'; -import { AuthBigInteger, BigInteger } from '../BigInteger'; -import { calculateS, calculateU } from '../calculate'; -import { getBytesFromHex } from '../getBytesFromHex'; -import { getHashFromData } from '../getHashFromData'; -import { getHashFromHex } from '../getHashFromHex'; -import { getHexFromBytes } from '../getHexFromBytes'; -import { getHkdfKey } from '../getHkdfKey'; -import { getPaddedHex } from '../getPaddedHex'; -import { getRandomBytes } from '../getRandomBytes'; -import { getRandomString } from '../getRandomString'; +import { AuthError } from '~/src/errors/AuthError'; +import { textEncoder } from '~/src/providers/cognito/utils/textEncoder'; +import { + AuthBigInteger, + BigInteger, +} from '~/src/providers/cognito/utils/srp/BigInteger'; +import { + calculateS, + calculateU, +} from '~/src/providers/cognito/utils/srp/calculate'; +import { getBytesFromHex } from '~/src/providers/cognito/utils/srp/getBytesFromHex'; +import { getHashFromData } from '~/src/providers/cognito/utils/srp/getHashFromData'; +import { getHashFromHex } from '~/src/providers/cognito/utils/srp/getHashFromHex'; +import { getHexFromBytes } from '~/src/providers/cognito/utils/srp/getHexFromBytes'; +import { getHkdfKey } from '~/src/providers/cognito/utils/srp/getHkdfKey'; +import { getPaddedHex } from '~/src/providers/cognito/utils/srp/getPaddedHex'; +import { getRandomBytes } from '~/src/providers/cognito/utils/srp/getRandomBytes'; +import { getRandomString } from '~/src/providers/cognito/utils/srp/getRandomString'; /** @class */ export default class AuthenticationHelper { @@ -48,7 +54,7 @@ export default class AuthenticationHelper { this.N = N; this.k = new BigInteger( getHashFromHex(`${getPaddedHex(N)}${getPaddedHex(g)}`), - 16 + 16, ); } @@ -62,6 +68,7 @@ export default class AuthenticationHelper { message: 'random password is empty', }); } + return this.randomPassword; } @@ -75,6 +82,7 @@ export default class AuthenticationHelper { message: 'saltToHashDevices is empty', }); } + return this.saltToHashDevices; } @@ -88,6 +96,7 @@ export default class AuthenticationHelper { message: 'verifyDevices is empty', }); } + return this.verifierDevices; } @@ -101,7 +110,7 @@ export default class AuthenticationHelper { */ async generateHashDevice( deviceGroupKey: string, - username: string + username: string, ): Promise { this.randomPassword = getRandomString(); const combinedString = `${deviceGroupKey}${username}:${this.randomPassword}`; @@ -116,18 +125,19 @@ export default class AuthenticationHelper { this.g.modPow( new BigInteger( getHashFromHex(this.saltToHashDevices + hashedString), - 16 + 16, ), this.N, (err: unknown, result: AuthBigInteger) => { if (err) { reject(err); + return; } this.verifierDevices = getPaddedHex(result); resolve(); - } + }, ); }); } @@ -165,7 +175,7 @@ export default class AuthenticationHelper { const x = new BigInteger( getHashFromHex(getPaddedHex(salt) + usernamePasswordHash), - 16 + 16, ); const S = await calculateS({ @@ -186,8 +196,9 @@ export default class AuthenticationHelper { const hkdfKey = getHkdfKey( getBytesFromHex(getPaddedHex(S)), getBytesFromHex(getPaddedHex(U)), - info + info, ); + return hkdfKey; } } diff --git a/packages/auth/src/providers/cognito/utils/srp/BigInteger/BigInteger.ts b/packages/auth/src/providers/cognito/utils/srp/BigInteger/BigInteger.ts index 634a2ec1c15..895dedd0999 100644 --- a/packages/auth/src/providers/cognito/utils/srp/BigInteger/BigInteger.ts +++ b/packages/auth/src/providers/cognito/utils/srp/BigInteger/BigInteger.ts @@ -1,4 +1,4 @@ -/* tslint:disable */ +/* eslint-disable */ // @ts-nocheck -> BigInteger is already a vended utility // A small implementation of BigInteger based on http://www-cs-students.stanford.edu/~tjw/jsbn/ // @@ -21,7 +21,10 @@ import { AuthBigInteger } from './types'; export default BigInteger as AuthBigInteger; -type BNP = { s: number; t: number }; +interface BNP { + s: number; + t: number; +} /* * Copyright (c) 2003-2005 Tom Wu * All Rights Reserved. @@ -84,13 +87,14 @@ function am1( w: number, j: number, c: number, - n: number + n: number, ): number { while (--n >= 0) { const v = x * this[i++] + w[j] + c; c = Math.floor(v / 0x4000000); w[j++] = v & 0x3ffffff; } + return c; } // am2 avoids a big mult-and-extract completely. @@ -102,10 +106,10 @@ function am2( w: number, j: number, c: number, - n: number + n: number, ): number { - const xl = x & 0x7fff, - xh = x >> 15; + const xl = x & 0x7fff; + const xh = x >> 15; while (--n >= 0) { let l = this[i] & 0x7fff; const h = this[i++] >> 15; @@ -114,6 +118,7 @@ function am2( c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30); w[j++] = l & 0x3fffffff; } + return c; } // Alternately, set max digit bits to 28 since some @@ -124,10 +129,10 @@ function am3( w: number, j: number, c: number, - n: number + n: number, ): number { - const xl = x & 0x3fff, - xh = x >> 14; + const xl = x & 0x3fff; + const xh = x >> 14; while (--n >= 0) { let l = this[i] & 0x3fff; const h = this[i++] >> 14; @@ -136,6 +141,7 @@ function am3( c = (l >> 28) + (m >> 14) + xh * h; w[j++] = l & 0xfffffff; } + return c; } const inBrowser = typeof navigator !== 'undefined'; @@ -162,7 +168,7 @@ BigInteger.prototype.F2 = 2 * dbits - BI_FP; // Digit conversions const BI_RM = '0123456789abcdefghijklmnopqrstuvwxyz'; -const BI_RC = new Array(); +const BI_RC = []; let rr: number, vv: number; rr = '0'.charCodeAt(0); for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; @@ -175,13 +181,14 @@ function int2char(n: number): string { return BI_RM.charAt(n); } function intAt(s: string, i: number): number { - var c = BI_RC[s.charCodeAt(i)]; + const c = BI_RC[s.charCodeAt(i)]; + return c == null ? -1 : c; } // (protected) copy this to r function bnpCopyTo(r: { t: number; s: number }): void { - for (var i = this.t - 1; i >= 0; --i) r[i] = this[i]; + for (let i = this.t - 1; i >= 0; --i) r[i] = this[i]; r.t = this.t; r.s = this.s; } @@ -197,7 +204,7 @@ function bnpFromInt(x: number): void { // return bigint initialized to value function nbv(i: number) { - var r = nbi(); + const r = nbi(); r.fromInt(i); @@ -215,9 +222,9 @@ function bnpFromString(s: string, b: number): void { else throw new Error('Only radix 2, 4, 8, 16, 32 are supported'); this.t = 0; this.s = 0; - let i = s.length, - mi = false, - sh = 0; + let i = s.length; + let mi = false; + let sh = 0; while (--i >= 0) { const x = intAt(s, i); if (x < 0) { @@ -239,25 +246,25 @@ function bnpFromString(s: string, b: number): void { // (protected) clamp off excess high words function bnpClamp(): void { - var c = this.s & this.DM; + const c = this.s & this.DM; while (this.t > 0 && this[this.t - 1] == c) --this.t; } // (public) return string representation in given radix function bnToString(b: number): string { if (this.s < 0) return '-' + this.negate().toString(b); - var k: number; + let k: number; if (b == 16) k = 4; else if (b === 8) k = 3; else if (b === 2) k = 1; else if (b === 32) k = 5; else if (b === 4) k = 2; else throw new Error('Only radix 2, 4, 8, 16, 32 are supported'); - let km = (1 << k) - 1, - d: number, - m = false, - r = '', - i = this.t; + const km = (1 << k) - 1; + let d: number; + let m = false; + let r = ''; + let i = this.t; let p = this.DB - ((i * this.DB) % k); if (i-- > 0) { if (p < this.DB && (d = this[i] >> p) > 0) { @@ -279,12 +286,13 @@ function bnToString(b: number): string { if (m) r += int2char(d); } } + return m ? r : '0'; } // (public) -this function bnNegate() { - var r = nbi(); + const r = nbi(); BigInteger.ZERO.subTo(this, r); @@ -298,19 +306,20 @@ function bnAbs() { // (public) return + if this > a, - if this < a, 0 if equal function bnCompareTo(a: BNP): number { - var r = this.s - a.s; + let r = this.s - a.s; if (r != 0) return r; - var i = this.t; + let i = this.t; r = i - a.t; if (r != 0) return this.s < 0 ? -r : r; while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; + return 0; } // returns bit length of the integer x function nbits(x: number): number { - var r = 1, - t: number; + let r = 1; + let t: number; if ((t = x >>> 16) !== 0) { x = t; r += 16; @@ -331,12 +340,14 @@ function nbits(x: number): number { x = t; r += 1; } + return r; } // (public) return the number of bits in "this" function bnBitLength(): number { if (this.t <= 0) return 0; + return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); } @@ -359,14 +370,14 @@ function bnpDRShiftTo(n: number, r: BNP): void { // (protected) r = this << n function bnpLShiftTo( n: number, - r: { s: number; t: number; clamp: Function } + r: { s: number; t: number; clamp: Function }, ): void { const bs = n % this.DB; const cbs = this.DB - bs; const bm = (1 << cbs) - 1; - let ds = Math.floor(n / this.DB), - c = (this.s << bs) & this.DM, - i; + const ds = Math.floor(n / this.DB); + let c = (this.s << bs) & this.DM; + let i; for (i = this.t - 1; i >= 0; --i) { r[i + ds + 1] = (this[i] >> cbs) | c; c = (this[i] & bm) << bs; @@ -384,6 +395,7 @@ function bnpRShiftTo(n: number, r: BNP & { clamp: Function }): void { const ds = Math.floor(n / this.DB); if (ds >= this.t) { r.t = 0; + return; } const bs = n % this.DB; @@ -401,9 +413,9 @@ function bnpRShiftTo(n: number, r: BNP & { clamp: Function }): void { // (protected) r = this - a function bnpSubTo(a: BNP, r: BNP & { clamp: Function }): void { - let i = 0, - c = 0, - m = Math.min(a.t, this.t); + let i = 0; + let c = 0; + const m = Math.min(a.t, this.t); while (i < m) { c += this[i] - a[i]; r[i++] = c & this.DM; @@ -437,10 +449,10 @@ function bnpSubTo(a: BNP, r: BNP & { clamp: Function }): void { // "this" should be the larger one if appropriate. function bnpMultiplyTo( a: BNP & { abs: Function }, - r: BNP & { clamp: Function } + r: BNP & { clamp: Function }, ): void { - const x = this.abs(), - y = a.abs(); + const x = this.abs(); + const y = a.abs(); let i = x.t; r.t = i + y.t; while (--i >= 0) r[i] = 0; @@ -452,11 +464,11 @@ function bnpMultiplyTo( // (protected) r = this^2, r != this (HAC 14.16) function bnpSquareTo(r) { - var x = this.abs(); - var i = (r.t = 2 * x.t); + const x = this.abs(); + let i = (r.t = 2 * x.t); while (--i >= 0) r[i] = 0; for (i = 0; i < x.t - 1; ++i) { - var c = x.am(i, x[i], r, 2 * i, 0, 1); + const c = x.am(i, x[i], r, 2 * i, 0, 1); if ( (r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV @@ -481,21 +493,22 @@ function bnpDivRemTo( drShiftTo: Function; clamp: Function; rShiftTo: Function; - } + }, ): void { - var pm = m.abs(); + const pm = m.abs(); if (pm.t <= 0) return; - var pt = this.abs(); + const pt = this.abs(); if (pt.t < pm.t) { if (q != null) q.fromInt(0); if (r != null) this.copyTo(r); + return; } if (r === null) r = nbi(); - var y = nbi(), - ts = this.s, - ms = m.s; - var nsh = this.DB - nbits(pm[pm.t - 1]); + const y = nbi(); + const ts = this.s; + const ms = m.s; + const nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus if (nsh > 0) { pm.lShiftTo(nsh, y); @@ -508,12 +521,12 @@ function bnpDivRemTo( const y0 = y[ys - 1]; if (y0 === 0) return; const yt = y0 * (1 << this.F1) + (ys > 1 ? y[ys - 2] >> this.F2 : 0); - const d1 = this.FV / yt, - d2 = (1 << this.F1) / yt, - e = 1 << this.F2; - let i = r.t, - j = i - ys, - t = q === null ? nbi() : q; + const d1 = this.FV / yt; + const d2 = (1 << this.F1) / yt; + const e = 1 << this.F2; + let i = r.t; + let j = i - ys; + const t = q === null ? nbi() : q; y.dlShiftTo(j, t); if (r.compareTo(t) >= 0) { r[r.t++] = 1; @@ -525,7 +538,7 @@ function bnpDivRemTo( while (y.t < ys) y[y.t++] = 0; while (--j >= 0) { // Estimate quotient digit - var qd = + let qd = r[--i] === y0 ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out @@ -547,9 +560,10 @@ function bnpDivRemTo( // (public) this mod a function bnMod(a) { - var r = nbi(); + const r = nbi(); this.abs().divRemTo(a, null, r); if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); + return r; } @@ -565,9 +579,9 @@ function bnMod(a) { // JS multiply "overflows" differently from C/C++, so care is needed here. function bnpInvDigit(): number { if (this.t < 1) return 0; - var x = this[0]; + const x = this[0]; if ((x & 1) === 0) return 0; - var y = x & 3; + let y = x & 3; // y == 1/x mod 2^2 y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 @@ -578,6 +592,7 @@ function bnpInvDigit(): number { // last step - calculate inverse mod DV directly; // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints y = (y * (2 - ((x * y) % this.DV))) % this.DV; + // y == 1/x mod 2^dbits // we really want the negative inverse, and -DV < y < DV return y > 0 ? this.DV - y : -y; @@ -589,9 +604,9 @@ function bnEquals(a: number): boolean { // (protected) r = this + a function bnpAddTo(a: BNP, r: BNP & { clamp: Function }): void { - let i = 0, - c = 0, - m = Math.min(a.t, this.t); + let i = 0; + let c = 0; + const m = Math.min(a.t, this.t); while (i < m) { c += this[i] + a[i]; r[i++] = c & this.DM; @@ -623,7 +638,7 @@ function bnpAddTo(a: BNP, r: BNP & { clamp: Function }): void { // (public) this + a function bnAdd(a: number) { - var r = nbi(); + const r = nbi(); this.addTo(a, r); @@ -632,7 +647,7 @@ function bnAdd(a: number) { // (public) this - a function bnSubtract(a: number): number { - var r = nbi(); + const r = nbi(); this.subTo(a, r); @@ -641,7 +656,7 @@ function bnSubtract(a: number): number { // (public) this * a function bnMultiply(a: number): number { - var r = nbi(); + const r = nbi(); this.multiplyTo(a, r); @@ -650,7 +665,7 @@ function bnMultiply(a: number): number { // (public) this / a function bnDivide(a: number): number { - var r = nbi(); + const r = nbi(); this.divRemTo(a, r, null); @@ -669,18 +684,20 @@ function Montgomery(m: { invDigit: Function; DB: number; t: number }): void { // xR mod m function montConvert(x: BNP & { abs: Function }): number { - var r = nbi(); + const r = nbi(); x.abs().dlShiftTo(this.m.t, r); r.divRemTo(this.m, null, r); if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); + return r; } // x/R mod m function montRevert(x: { copyTo: Function }): number { - var r = nbi(); + const r = nbi(); x.copyTo(r); this.reduce(r); + return r; } @@ -697,10 +714,10 @@ function montReduce(x: { while (x.t <= this.mt2) // pad x so am has enough room later x[x.t++] = 0; - for (var i = 0; i < this.m.t; ++i) { + for (let i = 0; i < this.m.t; ++i) { // faster way of calculating u0 = x[i]*mp mod DV - var j = x[i] & 0x7fff; - var u0 = + let j = x[i] & 0x7fff; + const u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; @@ -746,12 +763,12 @@ function bnModPow( DB: number; t: number; }, - callback: Function + callback: Function, ) { - let i = e.bitLength(), - k: number, - r = nbv(1), - z = new Montgomery(m); + let i = e.bitLength(); + let k: number; + let r = nbv(1); + const z = new Montgomery(m); if (i <= 0) return r; else if (i < 18) k = 1; else if (i < 48) k = 3; @@ -760,10 +777,10 @@ function bnModPow( else k = 6; // precomputation - let g = new Array(), - n = 3, - k1 = k - 1, - km = (1 << k) - 1; + const g = []; + let n = 3; + const k1 = k - 1; + const km = (1 << k) - 1; g[1] = z.convert(this); if (k > 1) { const g2 = nbi(); @@ -775,11 +792,11 @@ function bnModPow( } } - let j = e.t - 1, - w: number, - is1 = true, - r2 = nbi(), - t: number; + let j = e.t - 1; + let w: number; + let is1 = true; + let r2 = nbi(); + let t: number; i = nbits(e[j]) - 1; while (j >= 0) { if (i >= k1) w = (e[j] >> (i - k1)) & km; @@ -827,8 +844,9 @@ function bnModPow( } } } - var result = z.revert(r); + const result = z.revert(r); callback(null, result); + return result; } diff --git a/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.native.ts b/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.native.ts index 826abb92f09..d90304bc7f6 100644 --- a/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.native.ts +++ b/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.native.ts @@ -1,5 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +/* eslint-disable */ import { computeModPow } from '@aws-amplify/react-native'; @@ -9,7 +10,7 @@ import { AuthBigInteger } from './types'; BigInteger.prototype.modPow = function modPow( e: AuthBigInteger, m: AuthBigInteger, - callback: Function + callback: Function, ) { computeModPow({ base: (this as unknown as AuthBigInteger).toString(16), diff --git a/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.ts b/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.ts index 8bb8d7e19cc..83be1d1b7fc 100644 --- a/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.ts +++ b/packages/auth/src/providers/cognito/utils/srp/BigInteger/index.ts @@ -2,5 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import BigInteger from './BigInteger'; + export { AuthBigInteger } from './types'; export { BigInteger }; diff --git a/packages/auth/src/providers/cognito/utils/srp/BigInteger/types.ts b/packages/auth/src/providers/cognito/utils/srp/BigInteger/types.ts index 55888d19fb2..27f21eb57f8 100644 --- a/packages/auth/src/providers/cognito/utils/srp/BigInteger/types.ts +++ b/packages/auth/src/providers/cognito/utils/srp/BigInteger/types.ts @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +/* eslint-disable */ export interface AuthBigInteger { new (a?: any, b?: any): AuthBigInteger; subtract: Function; @@ -13,6 +14,6 @@ export interface AuthBigInteger { ZERO: any; abs: Function; compareTo: Function; - fromInt: (num: number) => void; - toString: (radix: number) => string; + fromInt(num: number): void; + toString(radix: number): string; } diff --git a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateA.ts b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateA.ts index ba8f1a99d4f..8b324a18745 100644 --- a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateA.ts +++ b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateA.ts @@ -1,7 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthBigInteger, BigInteger } from '../BigInteger'; +import { + AuthBigInteger, + BigInteger, +} from '~/src/providers/cognito/utils/srp/BigInteger'; /** * @internal @@ -19,11 +22,13 @@ export const calculateA = async ({ g.modPow(a, N, (err: unknown, A: AuthBigInteger) => { if (err) { reject(err); + return; } if (A.mod(N).equals(BigInteger.ZERO)) { reject(new Error('Illegal parameter. A mod N cannot be 0.')); + return; } diff --git a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.native.ts b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.native.ts index 8e42da77e36..f773d8dc782 100644 --- a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.native.ts +++ b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.native.ts @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { computeS } from '@aws-amplify/react-native'; -import { AuthBigInteger, BigInteger } from '../BigInteger'; +import { + AuthBigInteger, + BigInteger, +} from '~/src/providers/cognito/utils/srp/BigInteger'; export const calculateS = async ({ a, @@ -10,7 +13,6 @@ export const calculateS = async ({ k, x, B, - N, U, }: { a: AuthBigInteger; @@ -29,5 +31,6 @@ export const calculateS = async ({ b: B.toString(16), u: U.toString(16), }); + return new BigInteger(result, 16); }; diff --git a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.ts b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.ts index 3537fec56fd..9ad2ec63d1a 100644 --- a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.ts +++ b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateS.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthBigInteger } from '../BigInteger'; +import { AuthBigInteger } from '~/src/providers/cognito/utils/srp/BigInteger'; /** * @internal @@ -27,6 +27,7 @@ export const calculateS = async ({ g.modPow(x, N, (outerErr: unknown, outerResult: AuthBigInteger) => { if (outerErr) { reject(outerErr); + return; } @@ -36,10 +37,11 @@ export const calculateS = async ({ (innerErr: unknown, innerResult: AuthBigInteger) => { if (innerErr) { reject(innerErr); + return; } resolve(innerResult.mod(N)); - } + }, ); }); }); diff --git a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateU.ts b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateU.ts index c73a95c692c..59f2e5798eb 100644 --- a/packages/auth/src/providers/cognito/utils/srp/calculate/calculateU.ts +++ b/packages/auth/src/providers/cognito/utils/srp/calculate/calculateU.ts @@ -1,9 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthBigInteger, BigInteger } from '../BigInteger'; -import { getHashFromHex } from '../getHashFromHex'; -import { getPaddedHex } from '../getPaddedHex'; +import { + AuthBigInteger, + BigInteger, +} from '~/src/providers/cognito/utils/srp/BigInteger'; +import { getHashFromHex } from '~/src/providers/cognito/utils/srp/getHashFromHex'; +import { getPaddedHex } from '~/src/providers/cognito/utils/srp/getPaddedHex'; /** * @internal @@ -17,7 +20,7 @@ export const calculateU = ({ }): AuthBigInteger => { const U = new BigInteger( getHashFromHex(getPaddedHex(A) + getPaddedHex(B)), - 16 + 16, ); if (U.equals(BigInteger.ZERO)) { diff --git a/packages/auth/src/providers/cognito/utils/srp/getBytesFromHex.ts b/packages/auth/src/providers/cognito/utils/srp/getBytesFromHex.ts index ebb9ec398ef..dfc1ff032ca 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getBytesFromHex.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getBytesFromHex.ts @@ -20,7 +20,7 @@ export const getBytesFromHex = (encoded: string): Uint8Array => { out[i / 2] = HEX_TO_SHORT[encodedByte]; } else { throw new Error( - `Cannot decode unrecognized sequence ${encodedByte} as hexadecimal` + `Cannot decode unrecognized sequence ${encodedByte} as hexadecimal`, ); } } diff --git a/packages/auth/src/providers/cognito/utils/srp/getHashFromData.ts b/packages/auth/src/providers/cognito/utils/srp/getHashFromData.ts index 89987bc0503..e99646bf62e 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getHashFromData.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getHashFromData.ts @@ -3,6 +3,7 @@ import { Sha256 } from '@aws-crypto/sha256-js'; import { SourceData } from '@smithy/types'; + import { getHexFromBytes } from './getHexFromBytes'; /** @@ -17,5 +18,6 @@ export const getHashFromData = (data: SourceData): string => { const hashedData = sha256.digestSync(); const hashHexFromUint8 = getHexFromBytes(hashedData); + return new Array(64 - hashHexFromUint8.length).join('0') + hashHexFromUint8; }; diff --git a/packages/auth/src/providers/cognito/utils/srp/getHkdfKey.ts b/packages/auth/src/providers/cognito/utils/srp/getHkdfKey.ts index 39810e7dba4..498a1b30e7d 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getHkdfKey.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getHkdfKey.ts @@ -17,7 +17,7 @@ import { Sha256 } from '@aws-crypto/sha256-js'; export const getHkdfKey = ( ikm: Uint8Array, salt: Uint8Array, - info: Uint8Array + info: Uint8Array, ): Uint8Array => { const awsCryptoHash = new Sha256(salt); awsCryptoHash.update(ikm); diff --git a/packages/auth/src/providers/cognito/utils/srp/getPaddedHex.ts b/packages/auth/src/providers/cognito/utils/srp/getPaddedHex.ts index 0195d8c3d6b..8baae17bbe2 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getPaddedHex.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getPaddedHex.ts @@ -56,13 +56,14 @@ export const getPaddedHex = (bigInt: AuthBigInteger): string => { .split('') .map((x: string) => { const invertedNibble = ~parseInt(x, 16) & 0xf; + return '0123456789ABCDEF'.charAt(invertedNibble); }) .join(''); /* After flipping the bits, add one to get the 2's complement representation */ const flippedBitsBI = new BigInteger(invertedNibbles, 16).add( - BigInteger.ONE + BigInteger.ONE, ); hexStr = flippedBitsBI.toString(16); diff --git a/packages/auth/src/providers/cognito/utils/srp/getRandomBytes.ts b/packages/auth/src/providers/cognito/utils/srp/getRandomBytes.ts index 58c4ceaf4ed..151f773519e 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getRandomBytes.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getRandomBytes.ts @@ -1,9 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getBytesFromHex } from './getBytesFromHex'; import { WordArray } from '@aws-amplify/core/internals/utils'; +import { getBytesFromHex } from './getBytesFromHex'; + /** * Returns a Uint8Array with a sequence of random nBytes * diff --git a/packages/auth/src/providers/cognito/utils/srp/getRandomString.ts b/packages/auth/src/providers/cognito/utils/srp/getRandomString.ts index 097fab42fde..7c32c8814f6 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getRandomString.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getRandomString.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { base64Encoder } from '@aws-amplify/core/internals/utils'; + import { getRandomBytes } from './getRandomBytes'; /** diff --git a/packages/auth/src/providers/cognito/utils/srp/getSignatureString.ts b/packages/auth/src/providers/cognito/utils/srp/getSignatureString.ts index b781bd282f5..39c84a1fa48 100644 --- a/packages/auth/src/providers/cognito/utils/srp/getSignatureString.ts +++ b/packages/auth/src/providers/cognito/utils/srp/getSignatureString.ts @@ -7,7 +7,7 @@ import { base64Decoder, base64Encoder, } from '@aws-amplify/core/internals/utils'; -import { textEncoder } from '../textEncoder'; +import { textEncoder } from '~/src/providers/cognito/utils/textEncoder'; export const getSignatureString = ({ userPoolName, @@ -31,28 +31,27 @@ export const getSignatureString = ({ bufUPIDaToB.byteLength + bufUNaToB.byteLength + bufSBaToB.byteLength + - bufDNaToB.byteLength + bufDNaToB.byteLength, ); bufConcat.set(bufUPIDaToB, 0); bufConcat.set(bufUNaToB, bufUPIDaToB.byteLength); bufConcat.set(bufSBaToB, bufUPIDaToB.byteLength + bufUNaToB.byteLength); bufConcat.set( bufDNaToB, - bufUPIDaToB.byteLength + bufUNaToB.byteLength + bufSBaToB.byteLength + bufUPIDaToB.byteLength + bufUNaToB.byteLength + bufSBaToB.byteLength, ); const awsCryptoHash = new Sha256(hkdf); awsCryptoHash.update(bufConcat); const resultFromAWSCrypto = awsCryptoHash.digestSync(); const signatureString = base64Encoder.convert(resultFromAWSCrypto); + return signatureString; }; const urlB64ToUint8Array = (base64String: string): Uint8Array => { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); - const base64 = (base64String + padding) - .replace(/\-/g, '+') - .replace(/_/g, '/'); + const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = base64Decoder.convert(base64); const outputArray = new Uint8Array(rawData.length); @@ -60,5 +59,6 @@ const urlB64ToUint8Array = (base64String: string): Uint8Array => { for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } + return outputArray; }; diff --git a/packages/auth/src/providers/cognito/utils/textEncoder/index.native.ts b/packages/auth/src/providers/cognito/utils/textEncoder/index.native.ts index ce6acecc866..681d5d87429 100644 --- a/packages/auth/src/providers/cognito/utils/textEncoder/index.native.ts +++ b/packages/auth/src/providers/cognito/utils/textEncoder/index.native.ts @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { loadBuffer } from '@aws-amplify/react-native'; + import { TextEncoder } from './types'; export const textEncoder: TextEncoder = { convert(input) { const Buffer = loadBuffer(); + return new Buffer(input, 'utf8'); }, }; diff --git a/packages/auth/src/providers/cognito/utils/types.ts b/packages/auth/src/providers/cognito/utils/types.ts index 176f77527fd..08b0a4f0261 100644 --- a/packages/auth/src/providers/cognito/utils/types.ts +++ b/packages/auth/src/providers/cognito/utils/types.ts @@ -7,16 +7,18 @@ import { AuthUserPoolConfig, CognitoUserPoolConfig, } from '@aws-amplify/core'; - -import { AuthError } from '../../../errors/AuthError'; -import { CognitoAuthTokens, DeviceMetadata } from '../tokenProvider/types'; +import { AuthError } from '~/src/errors/AuthError'; +import { + CognitoAuthTokens, + DeviceMetadata, +} from '~/src/providers/cognito/tokenProvider/types'; import { DEVICE_METADATA_NOT_FOUND_EXCEPTION, USER_UNAUTHENTICATED_EXCEPTION, -} from '../../../errors/constants'; +} from '~/src/errors/constants'; export function isTypeUserPoolConfig( - authConfig?: AuthConfig + authConfig?: AuthConfig, ): authConfig is AuthUserPoolConfig { if ( authConfig && @@ -30,7 +32,7 @@ export function isTypeUserPoolConfig( } export function assertAuthTokens( - tokens?: AuthTokens | null + tokens?: AuthTokens | null, ): asserts tokens is AuthTokens { if (!tokens || !tokens.accessToken) { throw new AuthError({ @@ -42,7 +44,7 @@ export function assertAuthTokens( } export function assertIdTokenInAuthTokens( - tokens?: AuthTokens + tokens?: AuthTokens, ): asserts tokens is AuthTokens { if (!tokens || !tokens.idToken) { throw new AuthError({ @@ -54,7 +56,7 @@ export function assertIdTokenInAuthTokens( } export function assertAuthTokensWithRefreshToken( - tokens?: CognitoAuthTokens | null + tokens?: CognitoAuthTokens | null, ): asserts tokens is CognitoAuthTokens & { refreshToken: string } { if (!tokens || !tokens.accessToken || !tokens.refreshToken) { throw new AuthError({ @@ -69,7 +71,7 @@ type NonNullableDeviceMetadata = DeviceMetadata & { deviceGroupKey: string; }; export function assertDeviceMetadata( - deviceMetadata?: DeviceMetadata | null + deviceMetadata?: DeviceMetadata | null, ): asserts deviceMetadata is NonNullableDeviceMetadata { if ( !deviceMetadata || @@ -104,7 +106,7 @@ export interface OAuthStore { }>; storeOAuthSignIn( oauthSignIn: boolean, - preferPrivateSession: boolean + preferPrivateSession: boolean, ): Promise; loadOAuthState(): Promise; storeOAuthState(state: string): Promise; diff --git a/packages/auth/src/providers/cognito/utils/userContextData.native.ts b/packages/auth/src/providers/cognito/utils/userContextData.native.ts index ff5d7f226d5..9268cba0d02 100644 --- a/packages/auth/src/providers/cognito/utils/userContextData.native.ts +++ b/packages/auth/src/providers/cognito/utils/userContextData.native.ts @@ -3,11 +3,7 @@ // TODO: add support after https://amazon-cognito-assets.us-east-1.amazoncognito.com/amazon-cognito-advanced-security-data.min.js can be imported -export function getUserContextData({ - username, - userPoolId, - userPoolClientId, -}: { +export function getUserContextData(_: { username: string; userPoolId: string; userPoolClientId: string; diff --git a/packages/auth/src/providers/cognito/utils/userContextData.ts b/packages/auth/src/providers/cognito/utils/userContextData.ts index c21cd8d061e..43b2a1f3d5a 100644 --- a/packages/auth/src/providers/cognito/utils/userContextData.ts +++ b/packages/auth/src/providers/cognito/utils/userContextData.ts @@ -23,13 +23,14 @@ export function getUserContextData({ const advancedSecurityData = amazonCognitoAdvancedSecurityData.getData( username, userPoolId, - userPoolClientId + userPoolClientId, ); if (advancedSecurityData) { const userContextData = { EncodedData: advancedSecurityData, }; + return userContextData; } diff --git a/packages/auth/src/types/inputs.ts b/packages/auth/src/types/inputs.ts index 2e326686ef4..2ea988e227d 100644 --- a/packages/auth/src/types/inputs.ts +++ b/packages/auth/src/types/inputs.ts @@ -2,21 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 import { - AuthUserAttributes, + AuthDevice, AuthUserAttribute, AuthUserAttributeKey, - AuthDevice, + AuthUserAttributes, } from './models'; import { AuthServiceOptions, AuthSignUpOptions } from './options'; -export type AuthConfirmResetPasswordInput< +export interface AuthConfirmResetPasswordInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { username: string; newPassword: string; confirmationCode: string; options?: ServiceOptions; -}; +} /** * The parameters for constructing a Resend Sign Up code input. @@ -24,34 +24,34 @@ export type AuthConfirmResetPasswordInput< * @param username - a standard username, potentially an email/phone number * @param options - optional parameters for the Sign Up process such as the plugin options */ -export type AuthResendSignUpCodeInput< +export interface AuthResendSignUpCodeInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { username: string; options?: ServiceOptions; -}; +} -export type AuthResetPasswordInput< +export interface AuthResetPasswordInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { username: string; options?: ServiceOptions; -}; +} -export type AuthSignInInput< +export interface AuthSignInInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { username: string; password?: string; options?: ServiceOptions; -}; -export type AuthSignOutInput = { +} +export interface AuthSignOutInput { global: boolean; -}; +} export type AuthProvider = 'Amazon' | 'Apple' | 'Facebook' | 'Google'; -export type AuthSignInWithRedirectInput = { +export interface AuthSignInWithRedirectInput { provider?: AuthProvider | { custom: string }; customState?: string; options?: { @@ -66,7 +66,7 @@ export type AuthSignInWithRedirectInput = { */ preferPrivateSession?: boolean; }; -}; +} /** * The parameters for constructing a Sign Up input. @@ -75,14 +75,14 @@ export type AuthSignInWithRedirectInput = { * @param password - the user's password * @param options - optional parameters for the Sign Up process, including user attributes */ -export type AuthSignUpInput< +export interface AuthSignUpInput< ServiceOptions extends AuthSignUpOptions = AuthSignUpOptions, -> = { +> { username: string; password: string; options?: ServiceOptions; -}; +} /** * Constructs a `confirmSignUp` input. @@ -91,13 +91,13 @@ export type AuthSignUpInput< * @param confirmationCode - the user's confirmation code sent to email or cellphone * @param options - optional parameters for the Sign Up process, including user attributes */ -export type AuthConfirmSignUpInput< +export interface AuthConfirmSignUpInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { username: string; confirmationCode: string; options?: ServiceOptions; -}; +} /** * Constructs a `confirmSignIn` input. * @@ -105,12 +105,12 @@ export type AuthConfirmSignUpInput< * the sign in process. * @param options - optional parameters for the Confirm Sign In process such as the service options */ -export type AuthConfirmSignInInput< +export interface AuthConfirmSignInInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { challengeResponse: string; options?: ServiceOptions; -}; +} /** * Constructs a `VerifyTOTPSetup` input. @@ -118,12 +118,12 @@ export type AuthConfirmSignInInput< * @param code - required parameter for verifying the TOTP setup. * @param options - optional parameters for the Verify TOTP Setup process such as the service options. */ -export type AuthVerifyTOTPSetupInput< +export interface AuthVerifyTOTPSetupInput< ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { code: string; options?: ServiceOptions; -}; +} /** * Constructs a `updatePassword` input. @@ -131,10 +131,10 @@ export type AuthVerifyTOTPSetupInput< * @param oldPassword - previous password used for `signIn` * @param newPassword - new password to be used for `signIn` */ -export type AuthUpdatePasswordInput = { +export interface AuthUpdatePasswordInput { oldPassword: string; newPassword: string; -}; +} /** * Constructs a `updateUserAttributes` input. @@ -142,26 +142,26 @@ export type AuthUpdatePasswordInput = { * @param userAttributes - the user attributes to be updated * @param options - optional parameters for the Update User Attributes process such as the service options. */ -export type AuthUpdateUserAttributesInput< +export interface AuthUpdateUserAttributesInput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { userAttributes: AuthUserAttributes; options?: ServiceOptions; -}; +} /** * Constructs a `updateUserAttributes` input. * @param userAttributes - the user attribute to be updated * @param options - optional parameters for the Update User Attributes process such as the service options. */ -export type AuthUpdateUserAttributeInput< +export interface AuthUpdateUserAttributeInput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { userAttribute: AuthUserAttribute; options?: ServiceOptions; -}; +} /* * Constructs a `verifyUserAttribute` input. @@ -170,9 +170,12 @@ export type AuthUpdateUserAttributeInput< * @param confirmationCode - the user attribute verification code sent to email or cellphone * */ -export type AuthConfirmUserAttributeInput< +export interface AuthConfirmUserAttributeInput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { userAttributeKey: UserAttributeKey; confirmationCode: string }; +> { + userAttributeKey: UserAttributeKey; + confirmationCode: string; +} /** * Constructs a `sendUserAttributeVerificationCode` request. @@ -180,28 +183,30 @@ export type AuthConfirmUserAttributeInput< * @param userAttributeKey - the user attribute key * @param options - optional parameters for the Resend Attribute Code process such as the service options. */ -export type AuthSendUserAttributeVerificationCodeInput< +export interface AuthSendUserAttributeVerificationCodeInput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, ServiceOptions extends AuthServiceOptions = AuthServiceOptions, -> = { +> { userAttributeKey: UserAttributeKey; options?: ServiceOptions; -}; +} /** * Constructs a `deleteUserAttributes` input. * * @param userAttributeKeys - the user attribute keys to be deleted */ -export type AuthDeleteUserAttributesInput< +export interface AuthDeleteUserAttributesInput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { userAttributeKeys: [UserAttributeKey, ...UserAttributeKey[]] }; +> { + userAttributeKeys: [UserAttributeKey, ...UserAttributeKey[]]; +} /** * Constructs a `forgetDevice` input. * * @param device - optional parameter to forget an external device */ -export type AuthForgetDeviceInput = { +export interface AuthForgetDeviceInput { device?: AuthDevice; -}; +} diff --git a/packages/auth/src/types/models.ts b/packages/auth/src/types/models.ts index 08ff0c4f016..73226e8d8d1 100644 --- a/packages/auth/src/types/models.ts +++ b/packages/auth/src/types/models.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SignInOutput } from '../providers/cognito'; +import { SignInOutput } from '~/src/providers/cognito'; import { AuthStandardAttributeKey } from '@aws-amplify/core/internals/utils'; /** * Additional data that may be returned from Auth APIs. */ -export type AuthAdditionalInfo = { [key: string]: string }; +export type AuthAdditionalInfo = Record; -export type AuthAnyAttribute = string & {}; +export type AuthAnyAttribute = string; /** * Denotes the medium over which a confirmation code was sent. @@ -19,35 +19,35 @@ export type AuthDeliveryMedium = 'EMAIL' | 'SMS' | 'PHONE' | 'UNKNOWN'; /** * Data describing the dispatch of a confirmation code. */ -export type AuthCodeDeliveryDetails< +export interface AuthCodeDeliveryDetails< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { destination?: string; deliveryMedium?: AuthDeliveryMedium; attributeName?: UserAttributeKey; -}; +} /** * Denotes the next step in the Reset Password process. */ export type AuthResetPasswordStep = 'CONFIRM_RESET_PASSWORD_WITH_CODE' | 'DONE'; -export type AuthNextResetPasswordStep< +export interface AuthNextResetPasswordStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { resetPasswordStep: AuthResetPasswordStep; additionalInfo?: AuthAdditionalInfo; codeDeliveryDetails: AuthCodeDeliveryDetails; -}; +} -export type AuthTOTPSetupDetails = { +export interface AuthTOTPSetupDetails { sharedSecret: string; - getSetupUri: (appName: string, accountName?: string) => URL; -}; + getSetupUri(appName: string, accountName?: string): URL; +} export type AuthMFAType = 'SMS' | 'TOTP'; export type AuthAllowedMFATypes = AuthMFAType[]; -export type ContinueSignInWithTOTPSetup = { +export interface ContinueSignInWithTOTPSetup { /** * Auth step requires user to set up TOTP as multifactor authentication by associating an authenticator app * and retrieving an OTP code. @@ -61,8 +61,8 @@ export type ContinueSignInWithTOTPSetup = { */ signInStep: 'CONTINUE_SIGN_IN_WITH_TOTP_SETUP'; totpSetupDetails: AuthTOTPSetupDetails; -}; -export type ConfirmSignInWithTOTPCode = { +} +export interface ConfirmSignInWithTOTPCode { /** * Auth step requires user to use TOTP as multifactor authentication by retriving an OTP code from authenticator app. * @@ -74,9 +74,9 @@ export type ConfirmSignInWithTOTPCode = { * ``` */ signInStep: 'CONFIRM_SIGN_IN_WITH_TOTP_CODE'; -}; +} -export type ContinueSignInWithMFASelection = { +export interface ContinueSignInWithMFASelection { /** * Auth step requires user to select an mfa option (SMS | TOTP) to continue with the sign-in flow. * @@ -89,9 +89,9 @@ export type ContinueSignInWithMFASelection = { */ signInStep: 'CONTINUE_SIGN_IN_WITH_MFA_SELECTION'; allowedMFATypes?: AuthAllowedMFATypes; -}; +} -export type ConfirmSignInWithCustomChallenge = { +export interface ConfirmSignInWithCustomChallenge { /** * Auth step requires user to respond to a custom challenge. * @@ -103,11 +103,11 @@ export type ConfirmSignInWithCustomChallenge = { */ signInStep: 'CONFIRM_SIGN_IN_WITH_CUSTOM_CHALLENGE'; additionalInfo?: AuthAdditionalInfo; -}; +} -export type ConfirmSignInWithNewPasswordRequired< +export interface ConfirmSignInWithNewPasswordRequired< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { /** * Auth step requires user to change their password with any required attributes. * @@ -128,9 +128,9 @@ export type ConfirmSignInWithNewPasswordRequired< */ signInStep: 'CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED'; missingAttributes?: UserAttributeKey[]; -}; +} -export type ConfirmSignInWithSMSCode = { +export interface ConfirmSignInWithSMSCode { /** * Auth step requires user to use SMS as multifactor authentication by retrieving a code sent to cellphone. * @@ -143,34 +143,34 @@ export type ConfirmSignInWithSMSCode = { */ signInStep: 'CONFIRM_SIGN_IN_WITH_SMS_CODE'; codeDeliveryDetails?: AuthCodeDeliveryDetails; -}; +} -export type ConfirmSignUpStep = { +export interface ConfirmSignUpStep { /** * Auth step requires to confirm user's sign-up. * * Try calling confirmSignUp. */ signInStep: 'CONFIRM_SIGN_UP'; -}; +} -export type ResetPasswordStep = { +export interface ResetPasswordStep { /** * Auth step requires user to change their password. * * Try calling resetPassword. */ signInStep: 'RESET_PASSWORD'; -}; +} -export type DoneSignInStep = { +export interface DoneSignInStep { /** * The sign-in process is complete. * * No further action is needed. */ signInStep: 'DONE'; -}; +} export type AuthNextSignInStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, @@ -197,12 +197,12 @@ export type AuthUserAttributes< /** * The interface of a user attribute. */ -export type AuthUserAttribute< +export interface AuthUserAttribute< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { attributeKey: UserAttributeKey; value: string; -}; +} /** * A user attribute key type consisting of standard OIDC claims or custom attributes. @@ -233,42 +233,42 @@ export type AuthNextSignUpStep< | DoneSignUpStep; export type AutoSignInCallback = () => Promise; -export type DoneSignUpStep = { +export interface DoneSignUpStep { signUpStep: 'DONE'; -}; +} -export type ConfirmSignUpSignUpStep< +export interface ConfirmSignUpSignUpStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { signUpStep: 'CONFIRM_SIGN_UP'; codeDeliveryDetails: AuthCodeDeliveryDetails; -}; +} -export type AutoSignInSignUpStep< +export interface AutoSignInSignUpStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { signUpStep: 'COMPLETE_AUTO_SIGN_IN'; codeDeliveryDetails?: AuthCodeDeliveryDetails; -}; +} -export type AuthNextUpdateAttributeStep< +export interface AuthNextUpdateAttributeStep< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { updateAttributeStep: AuthUpdateAttributeStep; codeDeliveryDetails?: AuthCodeDeliveryDetails; -}; +} /** * The AWSAuthUser object contains username and userId from the idToken. */ -export type AWSAuthUser = { +export interface AWSAuthUser { username: string; userId: string; -}; +} /** * The AuthDevice object contains id and name of the device. */ -export type AuthDevice = { +export interface AuthDevice { id: string; -}; +} diff --git a/packages/auth/src/types/options.ts b/packages/auth/src/types/options.ts index d026ef3bfb6..2302cd2624f 100644 --- a/packages/auth/src/types/options.ts +++ b/packages/auth/src/types/options.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthUserAttributes, AuthUserAttributeKey } from './models'; +import { AuthUserAttributeKey, AuthUserAttributes } from './models'; /** * Base type for service options. @@ -14,10 +14,10 @@ export type AuthServiceOptions = Record; * @remarks * Particular services may require some of these parameters. */ -export type AuthSignUpOptions< +export interface AuthSignUpOptions< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { userAttributes: AuthUserAttributes; -}; +} export type SignInWithWebUIOptions = ServiceOptions; diff --git a/packages/auth/src/types/outputs.ts b/packages/auth/src/types/outputs.ts index 5e1d65cd3be..c21b44991eb 100644 --- a/packages/auth/src/types/outputs.ts +++ b/packages/auth/src/types/outputs.ts @@ -2,41 +2,41 @@ // SPDX-License-Identifier: Apache-2.0 import { - AuthUserAttributeKey, + AuthNextResetPasswordStep, AuthNextSignInStep, AuthNextSignUpStep, - AuthNextResetPasswordStep, AuthNextUpdateAttributeStep, + AuthUserAttributeKey, } from './models'; -export type AuthSignInOutput< +export interface AuthSignInOutput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { isSignedIn: boolean; nextStep: AuthNextSignInStep; -}; +} -export type AuthSignUpOutput< +export interface AuthSignUpOutput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { isSignUpComplete: boolean; userId?: string; nextStep: AuthNextSignUpStep; -}; +} -export type AuthResetPasswordOutput< +export interface AuthResetPasswordOutput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { isPasswordReset: boolean; nextStep: AuthNextResetPasswordStep; -}; +} -export type AuthUpdateUserAttributeOutput< +export interface AuthUpdateUserAttributeOutput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, -> = { +> { isUpdated: boolean; nextStep: AuthNextUpdateAttributeStep; -}; +} export type AuthUpdateUserAttributesOutput< UserAttributeKey extends AuthUserAttributeKey = AuthUserAttributeKey, diff --git a/packages/auth/src/utils/getAuthUserAgentDetails.ts b/packages/auth/src/utils/getAuthUserAgentDetails.ts index f23ff7622c9..9ad37a92b7d 100644 --- a/packages/auth/src/utils/getAuthUserAgentDetails.ts +++ b/packages/auth/src/utils/getAuthUserAgentDetails.ts @@ -9,7 +9,7 @@ import { export const getAuthUserAgentDetails = ( action: AuthAction, - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ): CustomUserAgentDetails => ({ category: Category.Auth, action, diff --git a/packages/auth/src/utils/getAuthUserAgentValue.ts b/packages/auth/src/utils/getAuthUserAgentValue.ts index b9ebb49aeb0..224b76d9eaa 100644 --- a/packages/auth/src/utils/getAuthUserAgentValue.ts +++ b/packages/auth/src/utils/getAuthUserAgentValue.ts @@ -10,7 +10,7 @@ import { export const getAuthUserAgentValue = ( action: AuthAction, - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ) => getAmplifyUserAgent({ category: Category.Auth, diff --git a/packages/auth/src/utils/openAuthSession.native.ts b/packages/auth/src/utils/openAuthSession.native.ts index a607d55edbd..24869ecc56a 100644 --- a/packages/auth/src/utils/openAuthSession.native.ts +++ b/packages/auth/src/utils/openAuthSession.native.ts @@ -2,22 +2,24 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAmplifyWebBrowser } from '@aws-amplify/react-native'; + import { OpenAuthSession, OpenAuthSessionResult } from './types'; export const openAuthSession: OpenAuthSession = async ( url: string, redirectUrls: string[], - prefersEphemeralSession?: boolean + prefersEphemeralSession?: boolean, ): Promise => { try { const redirectUrl = await loadAmplifyWebBrowser().openAuthSessionAsync( url, redirectUrls, - prefersEphemeralSession + prefersEphemeralSession, ); if (!redirectUrl) { return { type: 'canceled' }; } + return { type: 'success', url: redirectUrl }; } catch (error) { return { type: 'error', error }; diff --git a/packages/auth/src/utils/types.ts b/packages/auth/src/utils/types.ts index 39d48a19d91..4b5094b35d6 100644 --- a/packages/auth/src/utils/types.ts +++ b/packages/auth/src/utils/types.ts @@ -9,16 +9,16 @@ export type OpenAuthSession = ( type OpenAuthSessionResultType = 'canceled' | 'success' | 'error'; -export type OpenAuthSessionResult = { +export interface OpenAuthSessionResult { type: OpenAuthSessionResultType; error?: unknown; url?: string; -}; +} -export type AmplifyWebBrowser = { - openAuthSessionAsync: ( +export interface AmplifyWebBrowser { + openAuthSessionAsync( url: string, redirectUrls: string[], prefersEphemeralSession?: boolean - ) => Promise; -}; + ): Promise; +} diff --git a/packages/auth/tsconfig.build.json b/packages/auth/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/auth/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json index a9f3435dd5a..b0d37b09e2f 100644 --- a/packages/auth/tsconfig.json +++ b/packages/auth/tsconfig.json @@ -1,10 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "types": ["jest", "node"], - "strict": true, - "noImplicitAny": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, "include": ["src"] } diff --git a/packages/auth/tsconfig.test.json b/packages/auth/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/auth/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/auth/tsconfig.watch.json b/packages/auth/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/auth/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/auth/tslint.json b/packages/auth/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/auth/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/aws-amplify/.eslintrc.js b/packages/aws-amplify/.eslintrc.js new file mode 100644 index 00000000000..fb6ec2a29a7 --- /dev/null +++ b/packages/aws-amplify/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/aws-amplify', + }, + ], + }, +}; diff --git a/packages/aws-amplify/jest.config.js b/packages/aws-amplify/jest.config.js deleted file mode 100644 index 7365a413e7c..00000000000 --- a/packages/aws-amplify/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 85, - functions: 66, - lines: 90, - statements: 91, - }, - }, - moduleNameMapper: { - uuid: require.resolve('uuid'), - }, -}; diff --git a/packages/aws-amplify/jest.config.mjs b/packages/aws-amplify/jest.config.mjs new file mode 100644 index 00000000000..3ee7196662a --- /dev/null +++ b/packages/aws-amplify/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 85, + functions: 44, + lines: 90, + statements: 91, + }, + }, + moduleNameMapper: { + uuid: requireResolve('uuid'), + }, +}); + +export default jestConfig; diff --git a/packages/aws-amplify/package.json b/packages/aws-amplify/package.json index b13c5d3b1a3..7d356a07024 100644 --- a/packages/aws-amplify/package.json +++ b/packages/aws-amplify/package.json @@ -224,20 +224,21 @@ }, "sideEffects": false, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm run clean && npm test && tsc && webpack -p", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m es6 --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m es6 --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "rimraf lib-esm lib dist", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", "generate-docs-local": "typedoc --out docs src", "generate-docs-root": "typedoc --out ../../docs src", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 93.26" + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 93.26" }, "repository": { "type": "git", @@ -275,8 +276,7 @@ }, "devDependencies": { "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createAWSCredentialsAndIdentityIdProvider.ts b/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createAWSCredentialsAndIdentityIdProvider.ts index 521ab0bc072..7760abef0e0 100644 --- a/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createAWSCredentialsAndIdentityIdProvider.ts +++ b/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createAWSCredentialsAndIdentityIdProvider.ts @@ -6,8 +6,8 @@ import { DefaultIdentityIdStore, } from '@aws-amplify/auth/cognito'; import { - CredentialsAndIdentityIdProvider, AuthConfig, + CredentialsAndIdentityIdProvider, KeyValueStorageInterface, } from '@aws-amplify/core'; @@ -20,10 +20,11 @@ import { */ export const createAWSCredentialsAndIdentityIdProvider = ( authConfig: AuthConfig, - keyValueStorage: KeyValueStorageInterface + keyValueStorage: KeyValueStorageInterface, ): CredentialsAndIdentityIdProvider => { const credentialsProvider = new CognitoAWSCredentialsAndIdentityIdProvider( - new DefaultIdentityIdStore(keyValueStorage) + new DefaultIdentityIdStore(keyValueStorage), ); + return credentialsProvider; }; diff --git a/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createUserPoolsTokenProvider.ts b/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createUserPoolsTokenProvider.ts index f739d28f0b5..8e7a0ee9ed3 100644 --- a/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createUserPoolsTokenProvider.ts +++ b/packages/aws-amplify/src/adapterCore/authProvidersFactories/cognito/createUserPoolsTokenProvider.ts @@ -20,7 +20,7 @@ import { */ export const createUserPoolsTokenProvider = ( authConfig: AuthConfig, - keyValueStorage: KeyValueStorageInterface + keyValueStorage: KeyValueStorageInterface, ): TokenProvider => { const authTokenStore = new DefaultTokenStore(); authTokenStore.setAuthConfig(authConfig); diff --git a/packages/aws-amplify/src/adapterCore/runWithAmplifyServerContext.ts b/packages/aws-amplify/src/adapterCore/runWithAmplifyServerContext.ts index 021bfbe0aac..9bb6d3b38e3 100644 --- a/packages/aws-amplify/src/adapterCore/runWithAmplifyServerContext.ts +++ b/packages/aws-amplify/src/adapterCore/runWithAmplifyServerContext.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { + AmplifyServer, createAmplifyServerContext, destroyAmplifyServerContext, - AmplifyServer, } from '@aws-amplify/core/internals/adapter-core'; /** @@ -22,7 +22,7 @@ export const runWithAmplifyServerContext: AmplifyServer.RunOperationWithContext async (amplifyConfig, libraryOptions, operation) => { const contextSpec = createAmplifyServerContext( amplifyConfig, - libraryOptions + libraryOptions, ); // run the operation with injecting the context diff --git a/packages/aws-amplify/src/adapterCore/storageFactories/createKeyValueStorageFromCookieStorageAdapter.ts b/packages/aws-amplify/src/adapterCore/storageFactories/createKeyValueStorageFromCookieStorageAdapter.ts index a7a33025bda..dffd9bc4752 100644 --- a/packages/aws-amplify/src/adapterCore/storageFactories/createKeyValueStorageFromCookieStorageAdapter.ts +++ b/packages/aws-amplify/src/adapterCore/storageFactories/createKeyValueStorageFromCookieStorageAdapter.ts @@ -17,7 +17,7 @@ const ONE_YEAR_IN_MS = 365 * 24 * 60 * 60 * 1000; * @returns An object that implements {@link KeyValueStorageInterface}. */ export const createKeyValueStorageFromCookieStorageAdapter = ( - cookieStorageAdapter: CookieStorage.Adapter + cookieStorageAdapter: CookieStorage.Adapter, ): KeyValueStorageInterface => { return { setItem(key, value) { @@ -26,14 +26,17 @@ export const createKeyValueStorageFromCookieStorageAdapter = ( ...defaultSetCookieOptions, expires: new Date(Date.now() + ONE_YEAR_IN_MS), }); + return Promise.resolve(); }, getItem(key) { const cookie = cookieStorageAdapter.get(key); + return Promise.resolve(cookie?.value ?? null); }, removeItem(key) { cookieStorageAdapter.delete(key); + return Promise.resolve(); }, clear() { diff --git a/packages/aws-amplify/src/analytics/index.ts b/packages/aws-amplify/src/analytics/index.ts index be0ceb9180b..8ce900eca6b 100644 --- a/packages/aws-amplify/src/analytics/index.ts +++ b/packages/aws-amplify/src/analytics/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /* -This file maps exports from `aws-amplify/analytics`. It provides access to the default Analytics provider and category +This file maps exports from `aws-amplify/analytics`. It provides access to the default Analytics provider and category utils. */ export * from '@aws-amplify/analytics'; diff --git a/packages/aws-amplify/src/in-app-messaging/pinpoint/index.ts b/packages/aws-amplify/src/in-app-messaging/pinpoint/index.ts index 3c031e7362c..5d6340ed298 100644 --- a/packages/aws-amplify/src/in-app-messaging/pinpoint/index.ts +++ b/packages/aws-amplify/src/in-app-messaging/pinpoint/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /* -This file maps exports from `aws-amplify/in-app-messaging/pinpoint`. +This file maps exports from `aws-amplify/in-app-messaging/pinpoint`. It provides access to the Pinpoint provider of the InAppMessaging sub-category. */ export * from '@aws-amplify/notifications/in-app-messaging/pinpoint'; diff --git a/packages/aws-amplify/src/initSingleton.ts b/packages/aws-amplify/src/initSingleton.ts index 748012baf14..785ed9fd214 100644 --- a/packages/aws-amplify/src/initSingleton.ts +++ b/packages/aws-amplify/src/initSingleton.ts @@ -11,9 +11,10 @@ import { LegacyConfig, parseAWSExports, } from '@aws-amplify/core/internals/utils'; + import { - cognitoUserPoolsTokenProvider, cognitoCredentialsProvider, + cognitoUserPoolsTokenProvider, } from './auth/cognito'; export const DefaultAmplify = { @@ -31,7 +32,7 @@ export const DefaultAmplify = { */ configure( resourceConfig: ResourcesConfig | LegacyConfig, - libraryOptions?: LibraryOptions + libraryOptions?: LibraryOptions, ): void { let resolvedResourceConfig: ResourcesConfig; @@ -44,13 +45,17 @@ export const DefaultAmplify = { // If no Auth config is provided, no special handling will be required, configure as is. // Otherwise, we can assume an Auth config is provided from here on. if (!resolvedResourceConfig.Auth) { - return Amplify.configure(resolvedResourceConfig, libraryOptions); + Amplify.configure(resolvedResourceConfig, libraryOptions); + + return; } // If Auth options are provided, always just configure as is. // Otherwise, we can assume no Auth libraryOptions were provided from here on. if (libraryOptions?.Auth) { - return Amplify.configure(resolvedResourceConfig, libraryOptions); + Amplify.configure(resolvedResourceConfig, libraryOptions); + + return; } // If no Auth libraryOptions were previously configured, then always add default providers. @@ -60,15 +65,18 @@ export const DefaultAmplify = { // TODO: allow configure with a public interface libraryOptions?.ssr ? new CookieStorage({ sameSite: 'lax' }) - : defaultStorage + : defaultStorage, ); - return Amplify.configure(resolvedResourceConfig, { + + Amplify.configure(resolvedResourceConfig, { ...libraryOptions, Auth: { tokenProvider: cognitoUserPoolsTokenProvider, credentialsProvider: cognitoCredentialsProvider, }, }); + + return; } // At this point, Auth libraryOptions would have been previously configured and no overriding @@ -80,13 +88,16 @@ export const DefaultAmplify = { // TODO: allow configure with a public interface libraryOptions.ssr ? new CookieStorage({ sameSite: 'lax' }) - : defaultStorage + : defaultStorage, ); } - return Amplify.configure(resolvedResourceConfig, { + + Amplify.configure(resolvedResourceConfig, { Auth: Amplify.libraryOptions.Auth, ...libraryOptions, }); + + return; } // Finally, if there were no libraryOptions given at all, we should simply not touch the currently diff --git a/packages/aws-amplify/src/push-notifications/pinpoint/index.ts b/packages/aws-amplify/src/push-notifications/pinpoint/index.ts index 271aa62fbba..f5dbc056b56 100644 --- a/packages/aws-amplify/src/push-notifications/pinpoint/index.ts +++ b/packages/aws-amplify/src/push-notifications/pinpoint/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 /* -This file maps exports from `aws-amplify/push-notifications/pinpoint`. +This file maps exports from `aws-amplify/push-notifications/pinpoint`. It provides access to the Pinpoint provider of the PushNotification sub-category. */ export * from '@aws-amplify/notifications/push-notifications/pinpoint'; diff --git a/packages/aws-amplify/tsconfig.build.json b/packages/aws-amplify/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/aws-amplify/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/aws-amplify/tsconfig.json b/packages/aws-amplify/tsconfig.json index aed335f0bbc..b0d37b09e2f 100644 --- a/packages/aws-amplify/tsconfig.json +++ b/packages/aws-amplify/tsconfig.json @@ -1,8 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "strict": true, - "noImplicitAny": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["src"] } diff --git a/packages/aws-amplify/tsconfig.test.json b/packages/aws-amplify/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/aws-amplify/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/aws-amplify/tsconfig.watch.json b/packages/aws-amplify/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/aws-amplify/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/aws-amplify/tslint.json b/packages/aws-amplify/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/aws-amplify/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/core/.eslintrc.js b/packages/core/.eslintrc.js new file mode 100644 index 00000000000..0dcfe166638 --- /dev/null +++ b/packages/core/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/core', + }, + ], + }, +}; diff --git a/packages/core/__tests__/HubClass.test.ts b/packages/core/__tests__/HubClass.test.ts index a227fb832c2..0ed919a9127 100644 --- a/packages/core/__tests__/HubClass.test.ts +++ b/packages/core/__tests__/HubClass.test.ts @@ -5,7 +5,7 @@ import { ConsoleLogger } from '../src'; describe('Symbol undefined before load Hub', () => { test('Symbol not supported', () => { const listener = jest.fn(() => {}); - const amplifySymbol = '@@amplify_default' as unknown as Symbol; + const amplifySymbol = '@@amplify_default' as unknown as symbol; const loggerSpy = jest.spyOn(ConsoleLogger.prototype, '_log'); Hub.listen('auth', listener); @@ -18,13 +18,13 @@ describe('Symbol undefined before load Hub', () => { message: 'User signout has taken place', }, 'Auth', - amplifySymbol + amplifySymbol, ); expect(listener).toHaveBeenCalled(); expect(loggerSpy).not.toHaveBeenCalledWith( 'WARN', - 'WARNING: auth is protected and dispatching on it can have unintended consequences' + 'WARNING: auth is protected and dispatching on it can have unintended consequences', ); }); }); diff --git a/packages/core/__tests__/singleton/Singleton.test.ts b/packages/core/__tests__/singleton/Singleton.test.ts index b79f338346e..efae03c84ca 100644 --- a/packages/core/__tests__/singleton/Singleton.test.ts +++ b/packages/core/__tests__/singleton/Singleton.test.ts @@ -264,10 +264,10 @@ describe('Session tests', () => { credentials: mockCredentials, }; }, - clearCredentialsAndIdentityId: () => {}, + clearCredentialsAndIdentityId: jest.fn(), }, }, - } + }, ); const session = await fetchAuthSession(); @@ -344,7 +344,7 @@ describe('Session tests', () => { }, identityId: 'identityIdValue', }; - } + }, ); const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3MTAyOTMxMzB9.YzDpgJsrB3z-ZU1XxMcXSQsMbgCzwH_e-_76rnfehh0'; @@ -360,7 +360,7 @@ describe('Session tests', () => { Auth: { credentialsProvider: { getCredentialsAndIdentityId: credentialsSpy, - clearCredentialsAndIdentityId: () => {}, + clearCredentialsAndIdentityId: jest.fn(), }, tokenProvider: { getTokens: spyTokenProvider, @@ -439,7 +439,7 @@ describe('Session tests', () => { }, identityId: 'identityIdValue', }; - } + }, ); const spyTokenProvider = jest.fn(async () => { @@ -450,7 +450,7 @@ describe('Session tests', () => { Auth: { credentialsProvider: { getCredentialsAndIdentityId: credentialsSpy, - clearCredentialsAndIdentityId: () => {}, + clearCredentialsAndIdentityId: jest.fn(), }, tokenProvider: { getTokens: spyTokenProvider, @@ -509,7 +509,7 @@ describe('Session tests', () => { tokenProvider: { getTokens: tokenProvider, }, - } + }, ); await auth.fetchAuthSession({ forceRefresh: true }); @@ -537,7 +537,7 @@ describe('Session tests', () => { tokenProvider: { getTokens: tokenProvider, }, - } + }, ); const action = async () => diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js deleted file mode 100644 index 858a5ddd5e8..00000000000 --- a/packages/core/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 75, - functions: 72, - lines: 88, - statements: 88, - }, - }, -}; diff --git a/packages/core/jest.config.mjs b/packages/core/jest.config.mjs new file mode 100644 index 00000000000..1b5910cfd0a --- /dev/null +++ b/packages/core/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 75, + functions: 72, + lines: 88, + statements: 88, + }, + }, +}); + +export default jestConfig; diff --git a/packages/core/package.json b/packages/core/package.json index dd7cb69b977..ba393f3137d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -16,21 +16,22 @@ "./dist/esm/Cache/index.mjs" ], "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run generate-version && npm run build:esm-cjs && npm run build:umd", "generate-version": "genversion src/Platform/version.ts --es6 --semi --source ../aws-amplify", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", "prepublishOnly": "npm run build", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 92.36" + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 92.36" }, "repository": { "type": "git", @@ -64,8 +65,7 @@ "@rollup/plugin-typescript": "11.1.5", "@types/js-cookie": "3.0.2", "genversion": "^2.2.0", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/core/src/BackgroundProcessManager/BackgroundProcessManager.ts b/packages/core/src/BackgroundProcessManager/BackgroundProcessManager.ts index 5c8d04ccb3f..cb8956ed241 100644 --- a/packages/core/src/BackgroundProcessManager/BackgroundProcessManager.ts +++ b/packages/core/src/BackgroundProcessManager/BackgroundProcessManager.ts @@ -29,13 +29,6 @@ export class BackgroundProcessManager { */ private jobs = new Set(); - /** - * Creates a new manager for promises, observables, and other types - * of work that may be running in the background. This manager provides - * a centralized mechanism to request termination and await completion. - */ - constructor() {} - /** * Executes an async `job` function, passing the return value through to * the caller, registering it as a running job in the manager. When the @@ -61,7 +54,7 @@ export class BackgroundProcessManager { */ add( job: (onTerminate: Promise) => Promise, - description?: string + description?: string, ): Promise; /** @@ -76,8 +69,8 @@ export class BackgroundProcessManager { * @returns Job promise hooks + onTerminate signaling promise */ add(description?: string): { - resolve: (value?: unknown) => void; - reject: (reason?: any) => void; + resolve(value?: unknown): void; + reject(reason?: any): void; onTerminate: Promise; }; @@ -96,7 +89,7 @@ export class BackgroundProcessManager { | string | BackgroundProcessManager | ((...args: any) => Promise), - optionalDescription?: string + optionalDescription?: string, ) { let job: | BackgroundProcessManager @@ -120,10 +113,10 @@ export class BackgroundProcessManager { } else if (typeof job === 'function') { return this.addFunction(job, description); } else if (job instanceof BackgroundProcessManager) { - return this.addManager(job, description); + this.addManager(job, description); } else { throw new Error( - 'If `job` is provided, it must be an Observable, Function, or BackgroundProcessManager.' + 'If `job` is provided, it must be an Observable, Function, or BackgroundProcessManager.', ); } } @@ -140,7 +133,7 @@ export class BackgroundProcessManager { */ addCleaner( clean: () => Promise, - description?: string + description?: string, ): () => Promise { const { resolve, onTerminate } = this.addHook(description); @@ -156,15 +149,17 @@ export class BackgroundProcessManager { private addFunction( job: () => Promise, - description?: string + description?: string, ): Promise; + private addFunction( job: (onTerminate: Promise) => Promise, - description?: string + description?: string, ): Promise; + private addFunction( job: (() => Promise) | ((onTerminate: Promise) => Promise), - description?: string + description?: string, ) { // the function we call when we want to try to terminate this job. let terminate; @@ -183,7 +178,7 @@ export class BackgroundProcessManager { this.registerPromise( jobResult, terminate as unknown as () => void, - description + description, ); } @@ -194,7 +189,7 @@ export class BackgroundProcessManager { } private addManager(manager: BackgroundProcessManager, description?: string) { - this.addCleaner(async () => await manager.close(), description); + this.addCleaner(async () => manager.close(), description); } /** @@ -210,33 +205,33 @@ export class BackgroundProcessManager { private addHook(description?: string) { // the resolve/reject functions we'll provide to the caller to signal // the state of the job. - let resolve!: (value?: unknown) => void; - let reject!: (reason?: any) => void; + let hookResolve!: (value?: unknown) => void; + let hookReject!: (reason?: any) => void; // the underlying promise we'll use to manage it, pretty much like // any other promise. - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; + const promise = new Promise((resolve, reject) => { + hookResolve = resolve; + hookReject = reject; }); // the function we call when we want to try to terminate this job. let terminate; // the promise the job can opt into listening to for termination. - const onTerminate = new Promise(resolveTerminate => { - terminate = resolveTerminate; + const onTerminate = new Promise(resolve => { + terminate = resolve; }); this.registerPromise( promise, terminate as unknown as () => void, - description + description, ); return { - resolve, - reject, + resolve: hookResolve, + reject: hookReject, onTerminate, }; } @@ -255,7 +250,7 @@ export class BackgroundProcessManager { private registerPromise>( promise: T, terminate: () => void, - description?: string + description?: string, ) { const jobEntry = { promise, terminate, description }; this.jobs.add(jobEntry); @@ -345,8 +340,8 @@ export class BackgroundProcessManager { `Pending jobs: [\n${this.pending .map(t => ' ' + t) .join(',\n')}\n]`, - ].join('\n') - ) + ].join('\n'), + ), ); } } @@ -382,7 +377,7 @@ export class BackgroundProcessManager { `Failed to send termination signal to job. Error: ${ (error as Error).message }`, - job + job, ); } } @@ -390,7 +385,7 @@ export class BackgroundProcessManager { // Use `allSettled()` because we want to wait for all to finish. We do // not want to stop waiting if there is a failure. this._closingPromise = Promise.allSettled( - Array.from(this.jobs).map(j => j.promise) + Array.from(this.jobs).map(j => j.promise), ); await this._closingPromise; diff --git a/packages/core/src/BackgroundProcessManager/types.ts b/packages/core/src/BackgroundProcessManager/types.ts index d3474f848ce..875473f4faf 100644 --- a/packages/core/src/BackgroundProcessManager/types.ts +++ b/packages/core/src/BackgroundProcessManager/types.ts @@ -25,7 +25,7 @@ export enum BackgroundProcessManagerState { * Completely internal to `BackgroundProcessManager`, and describes the structure of * an entry in the jobs registry. */ -export type JobEntry = { +export interface JobEntry { /** * The underlying promise provided by the job function to wait for. */ @@ -34,7 +34,7 @@ export type JobEntry = { /** * Request the termination of the job. */ - terminate: () => void; + terminate(): void; /** * An object provided by the caller that can be used to identify the description @@ -46,4 +46,4 @@ export type JobEntry = { * on `close()`. */ description?: string; -}; +} diff --git a/packages/core/src/Cache/StorageCache.native.ts b/packages/core/src/Cache/StorageCache.native.ts index fa703d185c3..99184cc1ce5 100644 --- a/packages/core/src/Cache/StorageCache.native.ts +++ b/packages/core/src/Cache/StorageCache.native.ts @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAsyncStorage } from '@aws-amplify/react-native'; -import { ConsoleLogger } from '../Logger'; +import { ConsoleLogger } from '~/src/Logger'; + import { defaultConfig } from './constants'; import { StorageCacheCommon } from './StorageCacheCommon'; import { Cache, CacheConfig } from './types'; @@ -38,6 +39,7 @@ export class StorageCache extends StorageCacheCommon implements Cache { keys.push(key.substring(this.config.keyPrefix.length)); } } + return keys; } @@ -46,6 +48,7 @@ export class StorageCache extends StorageCacheCommon implements Cache { return AsyncStorage.getAllKeys(); } catch (e) { logger.warn(`getAllKeys failed! ${e}`); + return []; } } @@ -60,6 +63,7 @@ export class StorageCache extends StorageCacheCommon implements Cache { logger.error('invalid keyPrefix, setting keyPrefix with timeStamp'); config.keyPrefix = getCurrentTime.toString(); } + return new StorageCache(config); } } diff --git a/packages/core/src/Cache/StorageCache.ts b/packages/core/src/Cache/StorageCache.ts index b80c87715f8..dda19099258 100644 --- a/packages/core/src/Cache/StorageCache.ts +++ b/packages/core/src/Cache/StorageCache.ts @@ -1,9 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../Logger'; -import { KeyValueStorage } from '../storage/KeyValueStorage'; -import { getLocalStorageWithFallback } from '../storage/utils'; +import { ConsoleLogger } from '~/src/Logger'; +import { KeyValueStorage } from '~/src/storage/KeyValueStorage'; +import { getLocalStorageWithFallback } from '~/src/storage/utils'; + import { defaultConfig } from './constants'; import { StorageCacheCommon } from './StorageCacheCommon'; import { Cache, CacheConfig } from './types'; @@ -42,6 +43,7 @@ export class StorageCache extends StorageCacheCommon implements Cache { keys.push(key.substring(this.config.keyPrefix.length)); } } + return keys; } diff --git a/packages/core/src/Cache/StorageCacheCommon.ts b/packages/core/src/Cache/StorageCacheCommon.ts index 89adff70953..8597fd3e2cd 100644 --- a/packages/core/src/Cache/StorageCacheCommon.ts +++ b/packages/core/src/Cache/StorageCacheCommon.ts @@ -1,12 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../Logger'; -import { KeyValueStorageInterface } from '../types'; +import { ConsoleLogger } from '~/src/Logger'; +import { KeyValueStorageInterface } from '~/src/types'; + import { currentSizeKey, defaultConfig } from './constants'; import { CacheConfig, CacheItem, CacheItemOptions } from './types'; -import { getCurrentSizeKey, getCurrentTime, getByteLength } from './utils'; -import { assert, CacheErrorCode } from './utils/errorHelpers'; +import { getByteLength, getCurrentSizeKey, getCurrentTime } from './utils'; +import { CacheErrorCode, assert } from './utils/errorHelpers'; const logger = new ConsoleLogger('StorageCache'); @@ -58,7 +59,7 @@ export abstract class StorageCacheCommon { if (config) { if ((config as CacheConfig).keyPrefix) { logger.warn( - 'keyPrefix can not be re-configured on an existing Cache instance.' + 'keyPrefix can not be re-configured on an existing Cache instance.', ); } @@ -79,15 +80,16 @@ export abstract class StorageCacheCommon { */ public async getCurrentCacheSize() { let size = await this.getStorage().getItem( - getCurrentSizeKey(this.config.keyPrefix) + getCurrentSizeKey(this.config.keyPrefix), ); if (!size) { await this.getStorage().setItem( getCurrentSizeKey(this.config.keyPrefix), - '0' + '0', ); size = '0'; } + return Number(size); } @@ -112,21 +114,23 @@ export abstract class StorageCacheCommon { public async setItem( key: string, value: any, - options?: Record + options?: Record, ): Promise { logger.debug( - `Set item: key is ${key}, value is ${value} with options: ${options}` + `Set item: key is ${key}, value is ${value} with options: ${options}`, ); if (!key || key === currentSizeKey) { logger.warn( - `Invalid key: should not be empty or reserved key: '${currentSizeKey}'` + `Invalid key: should not be empty or reserved key: '${currentSizeKey}'`, ); + return; } if (typeof value === 'undefined') { logger.warn(`The value of item should not be undefined!`); + return; } @@ -143,8 +147,9 @@ export abstract class StorageCacheCommon { if (cacheItemOptions.priority < 1 || cacheItemOptions.priority > 5) { logger.warn( - `Invalid parameter: priority due to out or range. It should be within 1 and 5.` + `Invalid parameter: priority due to out or range. It should be within 1 and 5.`, ); + return; } @@ -154,8 +159,9 @@ export abstract class StorageCacheCommon { // check whether this item is too big; if (item.byteSize > this.config.itemMaxSize) { logger.warn( - `Item with key: ${key} you are trying to put into is too big!` + `Item with key: ${key} you are trying to put into is too big!`, ); + return; } @@ -174,6 +180,7 @@ export abstract class StorageCacheCommon { await this.popOutItems(validKeys, sizeToPop); } } + // put item in the cache return this.setCacheItem(prefixedKey, item); } catch (e) { @@ -198,15 +205,16 @@ export abstract class StorageCacheCommon { */ public async getItem( key: string, - options?: CacheItemOptions + options?: CacheItemOptions, ): Promise { logger.debug(`Get item: key is ${key} with options ${options}`); let cached; if (!key || key === currentSizeKey) { logger.warn( - `Invalid key: should not be empty or reserved key: '${currentSizeKey}'` + `Invalid key: should not be empty or reserved key: '${currentSizeKey}'`, ); + return null; } @@ -222,8 +230,9 @@ export abstract class StorageCacheCommon { // if not expired, update its visitedTime and return the value const item = await this.updateVisitedTime( JSON.parse(cached), - prefixedKey + prefixedKey, ); + return item.data; } } @@ -233,11 +242,14 @@ export abstract class StorageCacheCommon { if (val !== null) { await this.setItem(key, val, options); } + return val; } + return null; } catch (e) { logger.warn(`getItem failed! ${e}`); + return null; } } @@ -254,8 +266,9 @@ export abstract class StorageCacheCommon { if (!key || key === currentSizeKey) { logger.warn( - `Invalid key: should not be empty or reserved key: '${currentSizeKey}'` + `Invalid key: should not be empty or reserved key: '${currentSizeKey}'`, ); + return; } @@ -282,6 +295,7 @@ export abstract class StorageCacheCommon { return await this.getAllCacheKeys(); } catch (e) { logger.warn(`getAllkeys failed! ${e}`); + return []; } } @@ -304,6 +318,7 @@ export abstract class StorageCacheCommon { if (getCurrentTime() >= item.expires) { return true; } + return false; } @@ -315,7 +330,7 @@ export abstract class StorageCacheCommon { */ protected async removeCacheItem( prefixedKey: string, - size?: number + size?: number, ): Promise { const item = await this.getStorage().getItem(prefixedKey); assert(item !== null, CacheErrorCode.NoCacheItem, `Key: ${prefixedKey}`); @@ -343,7 +358,7 @@ export abstract class StorageCacheCommon { protected fillCacheItem( key: string, value: object | number | string | boolean, - options: CacheItemOptions + options: CacheItemOptions, ): CacheItem { const item: CacheItem = { key, @@ -359,20 +374,21 @@ export abstract class StorageCacheCommon { item.byteSize = getByteLength(JSON.stringify(item)); // re-calculate using cache item with updated byteSize property item.byteSize = getByteLength(JSON.stringify(item)); + return item; } private sanitizeConfig(): void { if (this.config.itemMaxSize > this.config.capacityInBytes) { logger.error( - 'Invalid parameter: itemMaxSize. It should be smaller than capacityInBytes. Setting back to default.' + 'Invalid parameter: itemMaxSize. It should be smaller than capacityInBytes. Setting back to default.', ); this.config.itemMaxSize = defaultConfig.itemMaxSize; } if (this.config.defaultPriority > 5 || this.config.defaultPriority < 1) { logger.error( - 'Invalid parameter: defaultPriority. It should be between 1 and 5. Setting back to default.' + 'Invalid parameter: defaultPriority. It should be between 1 and 5. Setting back to default.', ); this.config.defaultPriority = defaultConfig.defaultPriority; } @@ -382,7 +398,7 @@ export abstract class StorageCacheCommon { Number(this.config.warningThreshold) < 0 ) { logger.error( - 'Invalid parameter: warningThreshold. It should be between 0 and 1. Setting back to default.' + 'Invalid parameter: warningThreshold. It should be between 0 and 1. Setting back to default.', ); this.config.warningThreshold = defaultConfig.warningThreshold; } @@ -391,7 +407,7 @@ export abstract class StorageCacheCommon { const cacheLimit: number = 5 * 1024 * 1024; if (this.config.capacityInBytes > cacheLimit) { logger.error( - 'Cache Capacity should be less than 5MB. Setting back to default. Setting back to default.' + 'Cache Capacity should be less than 5MB. Setting back to default. Setting back to default.', ); this.config.capacityInBytes = defaultConfig.capacityInBytes; } @@ -406,7 +422,7 @@ export abstract class StorageCacheCommon { const size = await this.getCurrentCacheSize(); await this.getStorage().setItem( getCurrentSizeKey(this.config.keyPrefix), - (size + amount).toString() + (size + amount).toString(), ); } @@ -419,7 +435,7 @@ export abstract class StorageCacheCommon { const size = await this.getCurrentCacheSize(); await this.getStorage().setItem( getCurrentSizeKey(this.config.keyPrefix), - (size - amount).toString() + (size - amount).toString(), ); } @@ -433,10 +449,11 @@ export abstract class StorageCacheCommon { */ private async updateVisitedTime( item: CacheItem, - prefixedKey: string + prefixedKey: string, ): Promise { item.visitedTime = getCurrentTime(); await this.getStorage().setItem(prefixedKey, JSON.stringify(item)); + return item; } @@ -449,7 +466,7 @@ export abstract class StorageCacheCommon { */ private async setCacheItem( prefixedKey: string, - item: CacheItem + item: CacheItem, ): Promise { // first try to update the current size of the cache. await this.increaseCurrentSizeInBytes(item.byteSize); @@ -476,6 +493,7 @@ export abstract class StorageCacheCommon { const spaceItemNeed = cur + itemSize - this.config.capacityInBytes; const cacheThresholdSpace = (1 - this.config.warningThreshold) * this.config.capacityInBytes; + return spaceItemNeed > cacheThresholdSpace ? spaceItemNeed : cacheThresholdSpace; @@ -490,6 +508,7 @@ export abstract class StorageCacheCommon { */ private async isCacheFull(itemSize: number): Promise { const cur = await this.getCurrentCacheSize(); + return itemSize + cur > this.config.capacityInBytes; } @@ -504,8 +523,8 @@ export abstract class StorageCacheCommon { private async popOutItems(keys: string[], sizeToPop: number): Promise { const items: any[] = []; let remainedSize = sizeToPop; - for (let i = 0; i < keys.length; i += 1) { - const val = await this.getStorage().getItem(keys[i]); + for (const key of keys) { + const val = await this.getStorage().getItem(key); if (val != null) { const item = JSON.parse(val); items.push(item); @@ -526,10 +545,10 @@ export abstract class StorageCacheCommon { } }); - for (let i = 0; i < items.length; i += 1) { + for (const item of items) { // pop out items until we have enough room for new item - await this.removeCacheItem(items[i].key, items[i].byteSize); - remainedSize -= items[i].byteSize; + await this.removeCacheItem(item.key, item.byteSize); + remainedSize -= item.byteSize; if (remainedSize <= 0) { return; } @@ -555,6 +574,7 @@ export abstract class StorageCacheCommon { remainingKeys.push(key); } } + return remainingKeys; } diff --git a/packages/core/src/Cache/types/cache.ts b/packages/core/src/Cache/types/cache.ts index f7e06448d1a..53d05ec0543 100644 --- a/packages/core/src/Cache/types/cache.ts +++ b/packages/core/src/Cache/types/cache.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { CacheConfig } from '../../singleton/Cache/types'; +import { CacheConfig } from '~/src/singleton/Cache/types'; + export { CacheConfig }; /** @@ -47,5 +48,5 @@ export interface CacheItem { export interface CacheItemOptions { priority?: number; expires?: number; - callback?: Function; + callback?(): unknown; } diff --git a/packages/core/src/Cache/utils/CacheList.ts b/packages/core/src/Cache/utils/CacheList.ts index eecd79c520e..a110e16faf5 100644 --- a/packages/core/src/Cache/utils/CacheList.ts +++ b/packages/core/src/Cache/utils/CacheList.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { assert, CacheErrorCode } from './errorHelpers'; +import { CacheErrorCode, assert } from './errorHelpers'; class DoubleLinkedNode { key: string; @@ -9,7 +9,7 @@ class DoubleLinkedNode { nextNode: DoubleLinkedNode | null; constructor(keyVal?: string) { - this.key = keyVal ? keyVal : ''; + this.key = keyVal || ''; this.prevNode = null; this.nextNode = null; } @@ -112,6 +112,7 @@ export class CacheList { */ public getLastItem(): string { assert(this.tail.prevNode !== null, CacheErrorCode.NullPreviousNode); + return this.tail.prevNode.key; } @@ -145,7 +146,7 @@ export class CacheList { */ public clearList(): void { for (const key of Object.keys(this.hashtable)) { - if (this.hashtable.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(this.hashtable, key)) { delete this.hashtable[key]; } } @@ -169,6 +170,7 @@ export class CacheList { */ public isHeadNode(key: string): boolean { const node = this.hashtable[key]; + return node.prevNode === this.head; } @@ -180,6 +182,7 @@ export class CacheList { */ public isTailNode(key: string): boolean { const node = this.hashtable[key]; + return node.nextNode === this.tail; } } diff --git a/packages/core/src/Cache/utils/cacheHelpers.ts b/packages/core/src/Cache/utils/cacheHelpers.ts index 90235802b12..9d67ff69502 100644 --- a/packages/core/src/Cache/utils/cacheHelpers.ts +++ b/packages/core/src/Cache/utils/cacheHelpers.ts @@ -1,14 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { currentSizeKey } from '../constants'; +import { currentSizeKey } from '~/src/Cache/constants'; /** * return the byte size of the string * @param str */ export function getByteLength(str: string): number { - let ret: number = 0; + let ret = 0; ret = str.length; for (let i = str.length; i >= 0; i -= 1) { @@ -32,6 +32,7 @@ export function getByteLength(str: string): number { */ export function getCurrentTime(): number { const currentTime = new Date(); + return currentTime.getTime(); } diff --git a/packages/core/src/Cache/utils/errorHelpers.ts b/packages/core/src/Cache/utils/errorHelpers.ts index f191ce4449b..632830510ae 100644 --- a/packages/core/src/Cache/utils/errorHelpers.ts +++ b/packages/core/src/Cache/utils/errorHelpers.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { createAssertionFunction } from '../../errors'; -import { AmplifyErrorMap, AssertionFunction } from '../../types'; +import { createAssertionFunction } from '~/src/errors'; +import { AmplifyErrorMap, AssertionFunction } from '~/src/types'; export enum CacheErrorCode { NoCacheItem = 'NoCacheItem', diff --git a/packages/core/src/Hub/index.ts b/packages/core/src/Hub/index.ts index e80b42ab0fb..1ad26b1ce2d 100644 --- a/packages/core/src/Hub/index.ts +++ b/packages/core/src/Hub/index.ts @@ -1,9 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../Logger'; -import { NO_HUBCALLBACK_PROVIDED_EXCEPTION } from '../constants'; -import { AmplifyError } from '../errors'; +import { ConsoleLogger } from '~/src/Logger'; +import { NO_HUBCALLBACK_PROVIDED_EXCEPTION } from '~/src/constants'; +import { AmplifyError } from '~/src/errors'; + import { AmplifyChannel, AmplifyEventData, @@ -19,7 +20,7 @@ export const AMPLIFY_SYMBOL = ( typeof Symbol !== 'undefined' ? Symbol('amplify_default') : '@@amplify_default' -) as Symbol; +) as symbol; const logger = new ConsoleLogger('Hub'); @@ -56,6 +57,7 @@ export class HubClass { const holder = this.listeners.get(channel); if (!holder) { logger.warn(`No listeners for ${channel}`); + return; } this.listeners.set(channel, [ @@ -77,14 +79,14 @@ export class HubClass { channel: Channel, payload: HubPayload, source?: string, - ampSymbol?: Symbol + ampSymbol?: symbol, ): void; dispatch( channel: string, payload: HubPayload, source?: string, - ampSymbol?: Symbol + ampSymbol?: symbol, ): void; dispatch< @@ -94,7 +96,7 @@ export class HubClass { channel: Channel | string, payload: HubPayload, source?: string, - ampSymbol?: Symbol + ampSymbol?: symbol, ): void { if ( typeof channel === 'string' && @@ -104,7 +106,7 @@ export class HubClass { if (!hasAccess) { logger.warn( - `WARNING: ${channel} is protected and dispatching on it can have unintended consequences` + `WARNING: ${channel} is protected and dispatching on it can have unintended consequences`, ); } } @@ -134,17 +136,17 @@ export class HubClass { */ listen< Channel extends AmplifyChannel, - EventData extends EventDataMap = EventDataMap, + _EventData extends EventDataMap = EventDataMap, >( channel: Channel, callback: HubCallback, - listenerName?: string + listenerName?: string, ): StopListenerCallback; listen( channel: string, callback: HubCallback, - listenerName?: string + listenerName?: string, ): StopListenerCallback; listen< @@ -153,7 +155,7 @@ export class HubClass { >( channel: Channel, callback: HubCallback, - listenerName: string = 'noname' + listenerName = 'noname', ): StopListenerCallback { let cb: HubCallback; if (typeof callback !== 'function') { @@ -176,13 +178,14 @@ export class HubClass { name: listenerName, callback: cb, }); + return () => { this._remove(channel, cb); }; } private _toListeners( - capsule: HubCapsule + capsule: HubCapsule, ) { const { channel, payload } = capsule; const holder = this.listeners.get(channel); @@ -199,9 +202,9 @@ export class HubClass { } } -/*We export a __default__ instance of HubClass to use it as a +/* We export a __default__ instance of HubClass to use it as a pseudo Singleton for the main messaging bus, however you can still create -your own instance of HubClass() for a separate "private bus" of events.*/ +your own instance of HubClass() for a separate "private bus" of events. */ export const Hub = new HubClass('__default__'); /** diff --git a/packages/core/src/Hub/types/AuthTypes.ts b/packages/core/src/Hub/types/AuthTypes.ts index 127acaf0f49..a8876e03f03 100644 --- a/packages/core/src/Hub/types/AuthTypes.ts +++ b/packages/core/src/Hub/types/AuthTypes.ts @@ -1,30 +1,30 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -type AuthUser = { +interface AuthUser { username: string; userId: string; -}; -type AuthError = { +} +interface AuthError { name: string; message: string; recoverySuggestion?: string; -}; +} export type AuthHubEventData = - /** Dispatched when a user signs in with an oauth provider such as Google.*/ + /** Dispatched when a user signs in with an oauth provider such as Google. */ | { event: 'signInWithRedirect' } - /** Dispatched when there is an error in the oauth flow process.*/ + /** Dispatched when there is an error in the oauth flow process. */ | { event: 'signInWithRedirect_failure'; data: { error?: AuthError }; } - /** Dispatched when auth tokens are successfully refreshed.*/ + /** Dispatched when auth tokens are successfully refreshed. */ | { event: 'tokenRefresh' } - /** Dispatched when there is an error in the refresh of tokens.*/ + /** Dispatched when there is an error in the refresh of tokens. */ | { event: 'tokenRefresh_failure' } - /** Dispatched when there is a customState passed in the options of the `signInWithRedirect` API.*/ + /** Dispatched when there is a customState passed in the options of the `signInWithRedirect` API. */ | { event: 'customOAuthState'; data: string } - /** Dispatched when the user is signed-in.*/ + /** Dispatched when the user is signed-in. */ | { event: 'signedIn'; data: AuthUser } - /** Dispatched after the user calls the `signOut` API successfully.*/ + /** Dispatched after the user calls the `signOut` API successfully. */ | { event: 'signedOut' }; diff --git a/packages/core/src/Hub/types/HubTypes.ts b/packages/core/src/Hub/types/HubTypes.ts index 2f4d59a589b..c4cb2870b07 100644 --- a/packages/core/src/Hub/types/HubTypes.ts +++ b/packages/core/src/Hub/types/HubTypes.ts @@ -11,25 +11,25 @@ export type IListener< callback: HubCallback; }[]; -export type EventDataMap = { event: string; data?: unknown }; +export interface EventDataMap { event: string; data?: unknown } -export type AmplifyEventData = { +export interface AmplifyEventData { auth: AuthHubEventData; [key: string]: EventDataMap; -}; +} export type AmplifyChannel = 'auth'; export type StopListenerCallback = () => void; -export type HubCapsule< +export interface HubCapsule< Channel extends string, EventData extends EventDataMap, -> = { +> { channel: Channel; payload: HubPayload; source?: string; patternInfo?: string[]; -}; +} export type HubCallback< Channel extends string = string, @@ -41,6 +41,6 @@ export type HubPayload = message?: string; }; -export type AmplifyHubCallbackMap = { +export interface AmplifyHubCallbackMap { auth: HubCallback; -}; +} diff --git a/packages/core/src/I18n/I18n.ts b/packages/core/src/I18n/I18n.ts index 91026ee18ba..dfd9706bfe0 100644 --- a/packages/core/src/I18n/I18n.ts +++ b/packages/core/src/I18n/I18n.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../Logger'; +import { ConsoleLogger } from '~/src/Logger'; + import { I18nConfig } from './types'; const logger = new ConsoleLogger('I18n'); @@ -25,13 +26,6 @@ export class I18n { */ _dict: Record = {}; - /** - * @constructor - * Initialize with configurations - * @param {Object} options - */ - constructor() {} - /** * Sets the default language from the configuration when required. */ @@ -97,12 +91,12 @@ export class I18n { return defVal; } - const lang_dict = this._dict[language]; - if (!lang_dict) { + const langDict = this._dict[language]; + if (!langDict) { return defVal; } - return lang_dict[key]; + return langDict[key]; } /** @@ -113,13 +107,13 @@ export class I18n { */ putVocabulariesForLanguage( language: string, - vocabularies: Record + vocabularies: Record, ) { - let lang_dict = this._dict[language]; - if (!lang_dict) { - lang_dict = this._dict[language] = {}; + let langDict = this._dict[language]; + if (!langDict) { + langDict = this._dict[language] = {}; } - this._dict[language] = { ...lang_dict, ...vocabularies }; + this._dict[language] = { ...langDict, ...vocabularies }; } /** @@ -129,7 +123,7 @@ export class I18n { * vocabularies of each language as value */ putVocabularies(vocabularies: Record>) { - Object.keys(vocabularies).map(key => { + Object.keys(vocabularies).forEach(key => { this.putVocabulariesForLanguage(key, vocabularies[key]); }); } diff --git a/packages/core/src/I18n/errorHelpers.ts b/packages/core/src/I18n/errorHelpers.ts index 051f89a62a4..4e566c2f20c 100644 --- a/packages/core/src/I18n/errorHelpers.ts +++ b/packages/core/src/I18n/errorHelpers.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { createAssertionFunction } from '../errors'; -import { AmplifyErrorMap, AssertionFunction } from '../types'; +import { createAssertionFunction } from '~/src/errors'; +import { AmplifyErrorMap, AssertionFunction } from '~/src/types'; export enum I18nErrorCode { NotConfigured = 'NotConfigured', diff --git a/packages/core/src/I18n/index.ts b/packages/core/src/I18n/index.ts index 97740114877..ac251419ee9 100644 --- a/packages/core/src/I18n/index.ts +++ b/packages/core/src/I18n/index.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { I18n as I18nClass } from './I18n'; +import { ConsoleLogger } from '~/src/Logger'; -import { ConsoleLogger } from '../Logger'; +import { I18n as I18nClass } from './I18n'; import { I18nConfig } from './types'; -import { assert, I18nErrorCode } from './errorHelpers'; +import { I18nErrorCode, assert } from './errorHelpers'; const logger = new ConsoleLogger('I18n'); @@ -61,7 +61,7 @@ export class I18n { I18n.checkConfig(); assert(!!_i18n, I18nErrorCode.NotConfigured); - return _i18n.setLanguage(lang); + _i18n.setLanguage(lang); } /** @@ -88,12 +88,12 @@ export class I18n { */ static putVocabulariesForLanguage( language: string, - vocabularies: Record + vocabularies: Record, ) { I18n.checkConfig(); assert(!!_i18n, I18nErrorCode.NotConfigured); - return _i18n.putVocabulariesForLanguage(language, vocabularies); + _i18n.putVocabulariesForLanguage(language, vocabularies); } /** @@ -107,7 +107,7 @@ export class I18n { I18n.checkConfig(); assert(!!_i18n, I18nErrorCode.NotConfigured); - return _i18n.putVocabularies(vocabularies); + _i18n.putVocabularies(vocabularies); } public static checkConfig() { diff --git a/packages/core/src/Logger/ConsoleLogger.ts b/packages/core/src/Logger/ConsoleLogger.ts index a55e034173d..5ac7c08a30f 100644 --- a/packages/core/src/Logger/ConsoleLogger.ts +++ b/packages/core/src/Logger/ConsoleLogger.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InputLogEvent, Logger, LoggingProvider, LogType } from './types'; -import { AWS_CLOUDWATCH_CATEGORY } from '../constants'; +import { AWS_CLOUDWATCH_CATEGORY } from '~/src/constants'; + +import { InputLogEvent, LogType, Logger, LoggingProvider } from './types'; const LOG_LEVELS: Record = { VERBOSE: 1, @@ -40,9 +41,10 @@ export class ConsoleLogger implements Logger { _ts() { const dt = new Date(); + return ( [this._padding(dt.getMinutes()), this._padding(dt.getSeconds())].join( - ':' + ':', ) + '.' + dt.getMilliseconds() @@ -65,16 +67,16 @@ export class ConsoleLogger implements Logger { * @param {string|object} msg - Logging message or object */ _log(type: LogType | string, ...msg: any) { - let logger_level_name = this.level; + let loggerLevelName = this.level; if (ConsoleLogger.LOG_LEVEL) { - logger_level_name = ConsoleLogger.LOG_LEVEL; + loggerLevelName = ConsoleLogger.LOG_LEVEL; } - if (typeof (window) !== 'undefined' && (window).LOG_LEVEL) { - logger_level_name = (window).LOG_LEVEL; + if (typeof (window as any) !== 'undefined' && (window as any).LOG_LEVEL) { + loggerLevelName = (window as any).LOG_LEVEL; } - const logger_level = LOG_LEVELS[logger_level_name]; - const type_level = LOG_LEVELS[type]; - if (!(type_level >= logger_level)) { + const loggerLevel = LOG_LEVELS[loggerLevelName]; + const typeLevel = LOG_LEVELS[type]; + if (!(typeLevel >= loggerLevel)) { // Do nothing if type is not greater than or equal to logger level (handle undefined) return; } diff --git a/packages/core/src/Mutex/Mutex.ts b/packages/core/src/Mutex/Mutex.ts index 512a4907679..05bbbe00083 100644 --- a/packages/core/src/Mutex/Mutex.ts +++ b/packages/core/src/Mutex/Mutex.ts @@ -33,7 +33,7 @@ export class Mutex implements MutexInterface { acquire(): Promise { const ticket = new Promise(resolve => - this._queue.push(resolve) + this._queue.push(resolve), ); if (!this._pending) { @@ -55,11 +55,15 @@ export class Mutex implements MutexInterface { } return Promise.resolve(result).then( - (x: T) => (release(), x), + (x: T) => { + release(); + + return x; + }, e => { release(); throw e; - } + }, ); }); } @@ -73,6 +77,6 @@ export class Mutex implements MutexInterface { } } - private _queue: Array<(release: MutexInterface.Releaser) => void> = []; + private _queue: ((release: MutexInterface.Releaser) => void)[] = []; private _pending = false; } diff --git a/packages/core/src/Mutex/types.ts b/packages/core/src/Mutex/types.ts index bfab4f59bc0..2ef8cc6e8e2 100644 --- a/packages/core/src/Mutex/types.ts +++ b/packages/core/src/Mutex/types.ts @@ -9,12 +9,8 @@ export interface MutexInterface { isLocked(): boolean; } -export namespace MutexInterface { - export interface Releaser { - (): void; - } +export declare namespace MutexInterface { + export type Releaser = () => void; - export interface Worker { - (): Promise | T; - } + export type Worker = () => Promise | T; } diff --git a/packages/core/src/Platform/customUserAgent.ts b/packages/core/src/Platform/customUserAgent.ts index a62bf6b9fcc..133883941ed 100644 --- a/packages/core/src/Platform/customUserAgent.ts +++ b/packages/core/src/Platform/customUserAgent.ts @@ -25,7 +25,7 @@ const customUserAgentState: CustomUserAgentStateMap = {}; * @param input - SetCustomUserAgentInput that defines custom state to apply to the specified APIs. */ export const setCustomUserAgent = ( - input: SetCustomUserAgentInput + input: SetCustomUserAgentInput, ): (() => void) => { // Save custom user-agent state & increment reference counter // TODO Remove `any` when we upgrade to TypeScript 5.2, see: https://github.com/microsoft/TypeScript/issues/44373 @@ -37,7 +37,7 @@ export const setCustomUserAgent = ( additionalDetails: input.additionalDetails, }, }), - customUserAgentState[input.category] ?? {} + customUserAgentState[input.category] ?? {}, ); // Callback that cleans up state for APIs recorded by this call @@ -70,6 +70,6 @@ export const setCustomUserAgent = ( export const getCustomUserAgent = ( category: string, - api: string + api: string, ): AdditionalDetails | undefined => customUserAgentState[category]?.[api]?.additionalDetails; diff --git a/packages/core/src/Platform/detectFramework.ts b/packages/core/src/Platform/detectFramework.ts index 5fe68ae2138..f6ace79f571 100644 --- a/packages/core/src/Platform/detectFramework.ts +++ b/packages/core/src/Platform/detectFramework.ts @@ -30,13 +30,14 @@ export const detectFramework = (): Framework => { } else { // The first run of detectFramework: // Every time we update the cache, call each observer function - frameworkChangeObservers.forEach(fcn => fcn()); + frameworkChangeObservers.forEach(fcn => { fcn(); }); } // Retry once for either Unknown type after a delay (explained below) resetTimeout(Framework.ServerSideUnknown, SSR_RESET_TIMEOUT); resetTimeout(Framework.WebUnknown, WEB_RESET_TIMEOUT); } + return frameworkCache; }; diff --git a/packages/core/src/Platform/detection/Angular.ts b/packages/core/src/Platform/detection/Angular.ts index b0bd50b4bd4..4a34675d8d1 100644 --- a/packages/core/src/Platform/detection/Angular.ts +++ b/packages/core/src/Platform/detection/Angular.ts @@ -7,12 +7,12 @@ import { documentExists, processExists, windowExists } from './helpers'; export function angularWebDetect() { const angularVersionSetInDocument = Boolean( - documentExists() && document.querySelector('[ng-version]') + documentExists() && document.querySelector('[ng-version]'), ); const angularContentSetInWindow = Boolean( - // @ts-ignore - windowExists() && typeof window['ng'] !== 'undefined' + windowExists() && typeof (window as any).ng !== 'undefined', ); + return angularVersionSetInDocument || angularContentSetInWindow; } @@ -20,7 +20,7 @@ export function angularSSRDetect() { return ( (processExists() && typeof process.env === 'object' && - process.env['npm_lifecycle_script']?.startsWith('ng ')) || + process.env.npm_lifecycle_script?.startsWith('ng ')) || false ); } diff --git a/packages/core/src/Platform/detection/Expo.ts b/packages/core/src/Platform/detection/Expo.ts index 3294cf72b86..eecda08e4ea 100644 --- a/packages/core/src/Platform/detection/Expo.ts +++ b/packages/core/src/Platform/detection/Expo.ts @@ -6,6 +6,5 @@ import { globalExists } from './helpers'; // Tested with expo 48 / react-native 0.71.3 export function expoDetect() { - // @ts-ignore - return globalExists() && typeof global['expo'] !== 'undefined'; + return globalExists() && typeof (global as any).expo !== 'undefined'; } diff --git a/packages/core/src/Platform/detection/Next.ts b/packages/core/src/Platform/detection/Next.ts index 45cdb08d356..1389bd3df4f 100644 --- a/packages/core/src/Platform/detection/Next.ts +++ b/packages/core/src/Platform/detection/Next.ts @@ -6,8 +6,11 @@ import { globalExists, keyPrefixMatch, windowExists } from './helpers'; // Tested with next 13.4 / react 18.2 export function nextWebDetect() { - // @ts-ignore - return windowExists() && window['next'] && typeof window['next'] === 'object'; + return ( + windowExists() && + (window as any).next && + typeof (window as any).next === 'object' + ); } export function nextSSRDetect() { diff --git a/packages/core/src/Platform/detection/Nuxt.ts b/packages/core/src/Platform/detection/Nuxt.ts index 2341c9ac418..670f28993a5 100644 --- a/packages/core/src/Platform/detection/Nuxt.ts +++ b/packages/core/src/Platform/detection/Nuxt.ts @@ -8,12 +8,13 @@ import { globalExists, windowExists } from './helpers'; export function nuxtWebDetect() { return ( windowExists() && - // @ts-ignore - (window['__NUXT__'] !== undefined || window['$nuxt'] !== undefined) + ((window as any).__NUXT__ !== undefined || + (window as any).$nuxt !== undefined) ); } export function nuxtSSRDetect() { - // @ts-ignore - return globalExists() && typeof global['__NUXT_PATHS__'] !== 'undefined'; + return ( + globalExists() && typeof (global as any).__NUXT_PATHS__ !== 'undefined' + ); } diff --git a/packages/core/src/Platform/detection/index.ts b/packages/core/src/Platform/detection/index.ts index 8d04322d5aa..052932e52c0 100644 --- a/packages/core/src/Platform/detection/index.ts +++ b/packages/core/src/Platform/detection/index.ts @@ -1,22 +1,22 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Framework } from '../types'; +import { Framework } from '~/src/Platform/types'; -import { reactWebDetect, reactSSRDetect } from './React'; -import { vueWebDetect, vueSSRDetect } from './Vue'; -import { svelteWebDetect, svelteSSRDetect } from './Svelte'; -import { nextWebDetect, nextSSRDetect } from './Next'; -import { nuxtWebDetect, nuxtSSRDetect } from './Nuxt'; -import { angularWebDetect, angularSSRDetect } from './Angular'; +import { reactSSRDetect, reactWebDetect } from './React'; +import { vueSSRDetect, vueWebDetect } from './Vue'; +import { svelteSSRDetect, svelteWebDetect } from './Svelte'; +import { nextSSRDetect, nextWebDetect } from './Next'; +import { nuxtSSRDetect, nuxtWebDetect } from './Nuxt'; +import { angularSSRDetect, angularWebDetect } from './Angular'; import { reactNativeDetect } from './ReactNative'; import { expoDetect } from './Expo'; import { webDetect } from './Web'; -type PlatformDetectionEntry = { +interface PlatformDetectionEntry { platform: Framework; - detectionMethod: () => boolean; -}; + detectionMethod(): boolean; +} // These are in the order of detection where when both are detectable, the early Framework will be reported const detectionMap: PlatformDetectionEntry[] = [ diff --git a/packages/core/src/Platform/index.ts b/packages/core/src/Platform/index.ts index ec69df9c505..5adace8f580 100644 --- a/packages/core/src/Platform/index.ts +++ b/packages/core/src/Platform/index.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { UserAgent as AWSUserAgent } from '@aws-sdk/types'; + import { CustomUserAgentDetails, Framework } from './types'; import { version } from './version'; import { detectFramework, observeFrameworkChanges } from './detectFramework'; -import { UserAgent as AWSUserAgent } from '@aws-sdk/types'; import { getCustomUserAgent } from './customUserAgent'; const BASE_USER_AGENT = `aws-amplify`; @@ -32,7 +33,6 @@ export const Platform = new PlatformBuilder(); export const getAmplifyUserAgentObject = ({ category, action, - framework, }: CustomUserAgentDetails = {}): AWSUserAgent => { const userAgent: AWSUserAgent = [[BASE_USER_AGENT, version]]; if (category) { @@ -54,12 +54,12 @@ export const getAmplifyUserAgentObject = ({ }; export const getAmplifyUserAgent = ( - customUserAgentDetails?: CustomUserAgentDetails + customUserAgentDetails?: CustomUserAgentDetails, ): string => { const userAgent = getAmplifyUserAgentObject(customUserAgentDetails); const userAgentString = userAgent .map(([agentKey, agentValue]) => - agentKey && agentValue ? `${agentKey}/${agentValue}` : agentKey + agentKey && agentValue ? `${agentKey}/${agentValue}` : agentKey, ) .join(' '); diff --git a/packages/core/src/Platform/types.ts b/packages/core/src/Platform/types.ts index 58f1b1e2ca8..96ca1de77ec 100644 --- a/packages/core/src/Platform/types.ts +++ b/packages/core/src/Platform/types.ts @@ -122,7 +122,7 @@ export enum StorageAction { GetUrl = '7', } -type ActionMap = { +interface ActionMap { [Category.Auth]: AuthAction; [Category.API]: ApiAction; [Category.Analytics]: AnalyticsAction; @@ -134,7 +134,7 @@ type ActionMap = { [Category.PubSub]: PubSubAction; [Category.PushNotification]: PushNotificationAction; [Category.Storage]: StorageAction; -}; +} type UserAgentDetailsWithCategory = CustomUserAgentDetailsBase & { @@ -142,9 +142,9 @@ type UserAgentDetailsWithCategory = action: T extends keyof ActionMap ? ActionMap[T] : never; }; -type CustomUserAgentDetailsBase = { +interface CustomUserAgentDetailsBase { framework?: Framework; -}; +} export type CustomUserAgentDetails = | (CustomUserAgentDetailsBase & { category?: never; action?: never }) @@ -174,29 +174,29 @@ export type CustomUserAgentStateMap = Record; export type AdditionalDetails = [string, string?][]; -export type StorageUserAgentInput = { +export interface StorageUserAgentInput { category: Category.Storage; apis: StorageAction[]; additionalDetails: AdditionalDetails; -}; +} -export type AuthUserAgentInput = { +export interface AuthUserAgentInput { category: Category.Auth; apis: AuthAction[]; additionalDetails: AdditionalDetails; -}; +} -export type InAppMessagingUserAgentInput = { +export interface InAppMessagingUserAgentInput { category: Category.InAppMessaging; apis: InAppMessagingAction[]; additionalDetails: AdditionalDetails; -}; +} -export type GeoUserAgentInput = { +export interface GeoUserAgentInput { category: Category.Geo; apis: GeoAction[]; additionalDetails: AdditionalDetails; -}; +} export type SetCustomUserAgentInput = | StorageUserAgentInput diff --git a/packages/core/src/Reachability/Reachability.native.ts b/packages/core/src/Reachability/Reachability.native.ts index dda3bd1770e..ee91a12828c 100644 --- a/packages/core/src/Reachability/Reachability.native.ts +++ b/packages/core/src/Reachability/Reachability.native.ts @@ -2,14 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { Observable } from 'rxjs'; import { loadNetInfo } from '@aws-amplify/react-native'; -import { ConsoleLogger } from '../Logger'; +import { ConsoleLogger } from '~/src/Logger'; + import { NetworkStatus } from './types'; const logger = new ConsoleLogger('Reachability', 'DEBUG'); export class Reachability { networkMonitor( - netInfo?: ReturnType + netInfo?: ReturnType, ): Observable { /** * Here netinfo refers to @react-native-community/netinfo @@ -20,9 +21,10 @@ export class Reachability { */ if (!(netInfo && netInfo.addEventListener)) { throw new Error( - 'NetInfo must be passed to networkMonitor to enable reachability in React Native' + 'NetInfo must be passed to networkMonitor to enable reachability in React Native', ); } + return new Observable(observer => { logger.log('subscribing to reachability in React Native'); @@ -35,7 +37,7 @@ export class Reachability { logger.log('Notifying reachability change', online); observer.next({ online }); } - } + }, ); return () => { diff --git a/packages/core/src/Reachability/Reachability.ts b/packages/core/src/Reachability/Reachability.ts index db77ccadc6c..18f3af5dffb 100644 --- a/packages/core/src/Reachability/Reachability.ts +++ b/packages/core/src/Reachability/Reachability.ts @@ -1,12 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +// TODO(eslint): update usage of deprecated API. import { CompletionObserver, Observable, from } from 'rxjs'; +import { isWebWorker } from '~/src/utils'; + import { NetworkStatus } from './types'; -import { isWebWorker } from '../utils'; export class Reachability { - private static _observers: Array> = []; + private static _observers: CompletionObserver[] = []; networkMonitor(_?: unknown): Observable { const globalObj = isWebWorker() @@ -20,8 +22,12 @@ export class Reachability { return new Observable(observer => { observer.next({ online: globalObj.navigator.onLine }); - const notifyOnline = () => observer.next({ online: true }); - const notifyOffline = () => observer.next({ online: false }); + const notifyOnline = () => { + observer.next({ online: true }); + }; + const notifyOffline = () => { + observer.next({ online: false }); + }; globalObj.addEventListener('online', notifyOnline); globalObj.addEventListener('offline', notifyOffline); @@ -33,7 +39,7 @@ export class Reachability { globalObj.removeEventListener('offline', notifyOffline); Reachability._observers = Reachability._observers.filter( - _observer => _observer !== observer + _observer => _observer !== observer, ); }; }); @@ -44,7 +50,7 @@ export class Reachability { for (const observer of this._observers) { if (observer.closed) { this._observers = this._observers.filter( - _observer => _observer !== observer + _observer => _observer !== observer, ); continue; } diff --git a/packages/core/src/Reachability/types.ts b/packages/core/src/Reachability/types.ts index 4520b3c27bd..ced2b691a79 100644 --- a/packages/core/src/Reachability/types.ts +++ b/packages/core/src/Reachability/types.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type NetworkStatus = { +export interface NetworkStatus { online: boolean; -}; +} diff --git a/packages/core/src/ServiceWorker/ServiceWorker.ts b/packages/core/src/ServiceWorker/ServiceWorker.ts index 6683b8a4fc1..3aba747b1fe 100644 --- a/packages/core/src/ServiceWorker/ServiceWorker.ts +++ b/packages/core/src/ServiceWorker/ServiceWorker.ts @@ -1,12 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../Logger'; -import { isBrowser } from '../utils'; -import { AmplifyError } from '../errors'; -import { assert, ServiceWorkerErrorCode } from './errorHelpers'; -import { record } from '../providers/pinpoint'; -import { Amplify, fetchAuthSession } from '../singleton'; +import { ConsoleLogger } from '~/src/Logger'; +import { isBrowser } from '~/src/utils'; +import { AmplifyError } from '~/src/errors'; +import { record } from '~/src/providers/pinpoint'; +import { Amplify, fetchAuthSession } from '~/src/singleton'; + +import { ServiceWorkerErrorCode, assert } from './errorHelpers'; /** * Provides a means to registering a service worker in the browser @@ -37,15 +38,13 @@ export class ServiceWorkerClass { // The AWS Amplify logger private _logger: ConsoleLogger = new ConsoleLogger('ServiceWorker'); - constructor() {} - /** * Get the currently active service worker */ get serviceWorker(): ServiceWorker { assert( this._serviceWorker !== undefined, - ServiceWorkerErrorCode.UndefinedInstance + ServiceWorkerErrorCode.UndefinedInstance, ); return this._serviceWorker; @@ -63,9 +62,10 @@ export class ServiceWorkerClass { * - resolve(ServiceWorkerRegistration) * - reject(Error) **/ - register(filePath: string = '/service-worker.js', scope: string = '/') { + register(filePath = '/service-worker.js', scope = '/') { this._logger.debug(`registering ${filePath}`); this._logger.debug(`registering service worker with scope ${scope}`); + return new Promise((resolve, reject) => { if (navigator && 'serviceWorker' in navigator) { navigator.serviceWorker @@ -83,26 +83,28 @@ export class ServiceWorkerClass { this._registration = registration; this._setupListeners(); this._logger.debug( - `Service Worker Registration Success: ${registration}` + `Service Worker Registration Success: ${registration}`, ); - return resolve(registration); + + resolve(registration); }) .catch(error => { this._logger.debug(`Service Worker Registration Failed ${error}`); - return reject( + + reject( new AmplifyError({ name: ServiceWorkerErrorCode.Unavailable, message: 'Service Worker not available', underlyingError: error, - }) + }), ); }); } else { - return reject( + reject( new AmplifyError({ name: ServiceWorkerErrorCode.Unavailable, message: 'Service Worker not available', - }) + }), ); } }); @@ -122,34 +124,36 @@ export class ServiceWorkerClass { enablePush(publicKey: string) { assert( this._registration !== undefined, - ServiceWorkerErrorCode.UndefinedRegistration + ServiceWorkerErrorCode.UndefinedRegistration, ); this._publicKey = publicKey; + return new Promise((resolve, reject) => { if (isBrowser()) { assert( this._registration !== undefined, - ServiceWorkerErrorCode.UndefinedRegistration + ServiceWorkerErrorCode.UndefinedRegistration, ); this._registration.pushManager.getSubscription().then(subscription => { if (subscription) { this._subscription = subscription; this._logger.debug( - `User is subscribed to push: ${JSON.stringify(subscription)}` + `User is subscribed to push: ${JSON.stringify(subscription)}`, ); resolve(subscription); } else { this._logger.debug(`User is NOT subscribed to push`); + return this._registration!.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: this._urlB64ToUint8Array(publicKey), }) - .then(subscription => { - this._subscription = subscription; + .then(newSubscription => { + this._subscription = newSubscription; this._logger.debug( - `User subscribed: ${JSON.stringify(subscription)}` + `User subscribed: ${JSON.stringify(newSubscription)}`, ); - resolve(subscription); + resolve(newSubscription); }) .catch(error => { this._logger.error(error); @@ -157,11 +161,11 @@ export class ServiceWorkerClass { } }); } else { - return reject( + reject( new AmplifyError({ name: ServiceWorkerErrorCode.Unavailable, message: 'Service Worker not available', - }) + }), ); } }); @@ -174,7 +178,7 @@ export class ServiceWorkerClass { private _urlB64ToUint8Array(base64String: string) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding) - .replace(/\-/g, '+') + .replace(/-/g, '+') .replace(/_/g, '/'); const rawData = window.atob(base64); @@ -183,6 +187,7 @@ export class ServiceWorkerClass { for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } + return outputArray; } @@ -197,7 +202,7 @@ export class ServiceWorkerClass { send(message: object | string) { if (this._serviceWorker) { this._serviceWorker.postMessage( - typeof message === 'object' ? JSON.stringify(message) : message + typeof message === 'object' ? JSON.stringify(message) : message, ); } } @@ -207,7 +212,7 @@ export class ServiceWorkerClass { * https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker/state **/ _setupListeners() { - this.serviceWorker.addEventListener('statechange', async event => { + this.serviceWorker.addEventListener('statechange', async _ => { const currentState = this.serviceWorker.state; this._logger.debug(`ServiceWorker statechange: ${currentState}`); diff --git a/packages/core/src/ServiceWorker/errorHelpers.ts b/packages/core/src/ServiceWorker/errorHelpers.ts index b3663ca6da8..ec404d4ca51 100644 --- a/packages/core/src/ServiceWorker/errorHelpers.ts +++ b/packages/core/src/ServiceWorker/errorHelpers.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { createAssertionFunction } from '../errors'; -import { AmplifyErrorMap, AssertionFunction } from '../types'; +import { createAssertionFunction } from '~/src/errors'; +import { AmplifyErrorMap, AssertionFunction } from '~/src/types'; export enum ServiceWorkerErrorCode { UndefinedInstance = 'UndefinedInstance', diff --git a/packages/core/src/Signer/DateUtils.ts b/packages/core/src/Signer/DateUtils.ts index 8ca84523046..124a32b4791 100644 --- a/packages/core/src/Signer/DateUtils.ts +++ b/packages/core/src/Signer/DateUtils.ts @@ -8,16 +8,16 @@ */ const FIVE_MINUTES_IN_MS = 1000 * 60 * 5; -type DateUtils = { +interface DateUtils { clockOffset: number; - getDateWithClockOffset: () => Date; - getClockOffset: () => number; - getHeaderStringFromDate: (date: Date) => string; - getDateFromHeaderString: (header: string) => Date; - isClockSkewed: (serverDate: Date) => boolean; - isClockSkewError: (error: any) => boolean; - setClockOffset: (offset: number) => void; -}; + getDateWithClockOffset(): Date; + getClockOffset(): number; + getHeaderStringFromDate(date: Date): string; + getDateFromHeaderString(header: string): Date; + isClockSkewed(serverDate: Date): boolean; + isClockSkewError(error: any): boolean; + setClockOffset(offset: number): void; +} /** * This utility is intended to be deprecated and replaced by `signRequest` and `presignUrl` functions from @@ -50,12 +50,12 @@ export const DateUtils: DateUtils = { }, getHeaderStringFromDate(date: Date = DateUtils.getDateWithClockOffset()) { - return date.toISOString().replace(/[:\-]|\.\d{3}/g, ''); + return date.toISOString().replace(/[:-]|\.\d{3}/g, ''); }, getDateFromHeaderString(header: string) { const [, year, month, day, hour, minute, second] = header.match( - /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2}).+/ + /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2}).+/, ) as any[]; return new Date( @@ -65,8 +65,8 @@ export const DateUtils: DateUtils = { Number(day), Number(hour), Number(minute), - Number(second) - ) + Number(second), + ), ); }, @@ -74,7 +74,7 @@ export const DateUtils: DateUtils = { // API gateway permits client calls that are off by no more than ±5 minutes return ( Math.abs( - serverDate.getTime() - DateUtils.getDateWithClockOffset().getTime() + serverDate.getTime() - DateUtils.getDateWithClockOffset().getTime(), ) >= FIVE_MINUTES_IN_MS ); }, @@ -88,9 +88,9 @@ export const DateUtils: DateUtils = { return Boolean( ['BadRequestException', 'InvalidSignatureException'].includes( - headers['x-amzn-errortype'] + headers['x-amzn-errortype'], ) && - (headers.date || headers.Date) + (headers.date || headers.Date), ); }, diff --git a/packages/core/src/Signer/Signer.ts b/packages/core/src/Signer/Signer.ts index 18b2713482c..d54f0db1b36 100644 --- a/packages/core/src/Signer/Signer.ts +++ b/packages/core/src/Signer/Signer.ts @@ -1,17 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { DateUtils } from './DateUtils'; import { + TOKEN_QUERY_PARAM, presignUrl, signRequest, - TOKEN_QUERY_PARAM, -} from '../clients/middleware/signing/signer/signatureV4'; -import { AmplifyUrl } from '../utils/amplifyUrl'; +} from '~/src/clients/middleware/signing/signer/signatureV4'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; + +// TODO(eslint): update usage of deprecated API. +import { DateUtils } from './DateUtils'; const IOT_SERVICE_NAME = 'iotdevicegateway'; // Best practice regex to parse the service and region from an AWS endpoint -const AWS_ENDPOINT_REGEX = /([^\.]+)\.(?:([^\.]*)\.)?amazonaws\.com(.cn)?$/; +const AWS_ENDPOINT_REGEX = /([^.]+)\.(?:([^.]*)\.)?amazonaws\.com(.cn)?$/; /** * This class is intended to be deprecated and replaced by `signRequest` and `presignUrl` functions from @@ -72,13 +74,13 @@ export class Signer { secret_key: string; session_token: string; }, - serviceInfo: { service: string; region: string } + serviceInfo: { service: string; region: string }, ) { request.headers = request.headers || {}; if (request.body && !request.data) { throw new Error( - 'The attribute "body" was found on the request object. Please use the attribute "data" instead.' + 'The attribute "body" was found on the request object. Please use the attribute "data" instead.', ); } @@ -100,6 +102,7 @@ export class Signer { signedRequest.headers['x-amz-security-token']; delete signedRequest.headers.authorization; delete signedRequest.headers['x-amz-security-token']; + return signedRequest; } @@ -107,19 +110,21 @@ export class Signer { urlToSign: string, accessInfo: any, serviceInfo?: any, - expiration?: number + expiration?: number, ): string; + static signUrl( request: any, accessInfo: any, serviceInfo?: any, - expiration?: number + expiration?: number, ): string; + static signUrl( urlOrRequest: string | any, accessInfo: any, serviceInfo?: any, - expiration?: number + expiration?: number, ): string { const urlToSign: string = typeof urlOrRequest === 'object' ? urlOrRequest.url : urlOrRequest; @@ -138,7 +143,7 @@ export class Signer { presignable, accessInfo, serviceInfo, - expiration + expiration, ); const signedUrl = presignUrl(presignable, options); if ( @@ -147,9 +152,10 @@ export class Signer { ) { signedUrl.searchParams.append( TOKEN_QUERY_PARAM, - accessInfo.session_token + accessInfo.session_token, ); } + return signedUrl.toString(); } } @@ -158,11 +164,11 @@ const getOptions = ( request: { url: URL } & Record, accessInfo: { access_key: string; secret_key: string; session_token: string }, serviceInfo: { service: string; region: string }, - expiration?: number + expiration?: number, ) => { const { access_key, secret_key, session_token } = accessInfo ?? {}; const { region: urlRegion, service: urlService } = parseServiceInfo( - request.url + request.url, ); const { region = urlRegion, service = urlService } = serviceInfo ?? {}; const credentials = { @@ -172,8 +178,10 @@ const getOptions = ( ? { sessionToken: session_token } : {}), }; + return { credentials, + // TODO(eslint): update usage of deprecated API. signingDate: DateUtils.getDateWithClockOffset(), signingRegion: region, signingService: service, @@ -182,7 +190,7 @@ const getOptions = ( }; const parseServiceInfo = (url: URL) => { - const host = url.host; + const { host } = url; const matched = host.match(AWS_ENDPOINT_REGEX) ?? []; let parsed = matched.slice(1, 3); diff --git a/packages/core/src/adapterCore/error/AmplifyServerContextError.ts b/packages/core/src/adapterCore/error/AmplifyServerContextError.ts index 3ebf2733f7a..5d809e751f0 100644 --- a/packages/core/src/adapterCore/error/AmplifyServerContextError.ts +++ b/packages/core/src/adapterCore/error/AmplifyServerContextError.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyError } from '../../errors'; +import { AmplifyError } from '~/src/errors'; export class AmplifyServerContextError extends AmplifyError { constructor({ diff --git a/packages/core/src/adapterCore/serverContext/serverContext.ts b/packages/core/src/adapterCore/serverContext/serverContext.ts index 6eb0767f4e7..dfe074a9fd4 100644 --- a/packages/core/src/adapterCore/serverContext/serverContext.ts +++ b/packages/core/src/adapterCore/serverContext/serverContext.ts @@ -1,11 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { AmplifyClass } from '~/src/singleton'; +import { LibraryOptions, ResourcesConfig } from '~/src/singleton/types'; +import { AmplifyServerContextError } from '~/src/adapterCore/error'; + import { AmplifyServer } from './types'; import { serverContextRegistry } from './serverContextRegistry'; -import { AmplifyClass } from '../../singleton'; -import { LibraryOptions, ResourcesConfig } from '../../singleton/types'; -import { AmplifyServerContextError } from '../error'; /** * Creates an Amplify server context. @@ -15,7 +16,7 @@ import { AmplifyServerContextError } from '../error'; */ export const createAmplifyServerContext = ( amplifyConfig: ResourcesConfig, - libraryOptions: LibraryOptions + libraryOptions: LibraryOptions, ): AmplifyServer.ContextSpec => { const amplify = new AmplifyClass(); amplify.configure(amplifyConfig, libraryOptions); @@ -31,7 +32,7 @@ export const createAmplifyServerContext = ( * @returns The Amplify server context. */ export const getAmplifyServerContext = ( - contextSpec: AmplifyServer.ContextSpec + contextSpec: AmplifyServer.ContextSpec, ): AmplifyServer.Context => { assertContextSpec(contextSpec); const context = serverContextRegistry.get(contextSpec); @@ -53,7 +54,7 @@ export const getAmplifyServerContext = ( * @param contextSpec The context spec used to destroy the Amplify server context. */ export const destroyAmplifyServerContext = ( - contextSpec: AmplifyServer.ContextSpec + contextSpec: AmplifyServer.ContextSpec, ): void => { serverContextRegistry.deregister(contextSpec); }; diff --git a/packages/core/src/adapterCore/serverContext/serverContextRegistry.ts b/packages/core/src/adapterCore/serverContext/serverContextRegistry.ts index 009510ba6a5..5f672333c90 100644 --- a/packages/core/src/adapterCore/serverContext/serverContextRegistry.ts +++ b/packages/core/src/adapterCore/serverContext/serverContextRegistry.ts @@ -18,13 +18,14 @@ export const serverContextRegistry = { register(context: AmplifyServer.Context): AmplifyServer.ContextSpec { const token = createToken(); storage.set(token, context); + return { token }; }, deregister(contextSpec: AmplifyServer.ContextSpec): boolean { return storage.delete(contextSpec.token); }, get( - contextSpec: AmplifyServer.ContextSpec + contextSpec: AmplifyServer.ContextSpec, ): AmplifyServer.Context | undefined { return storage.get(contextSpec.token); }, diff --git a/packages/core/src/adapterCore/serverContext/types/amplifyServer.ts b/packages/core/src/adapterCore/serverContext/types/amplifyServer.ts index 5d7a2348ae0..e0da4dc840e 100644 --- a/packages/core/src/adapterCore/serverContext/types/amplifyServer.ts +++ b/packages/core/src/adapterCore/serverContext/types/amplifyServer.ts @@ -1,29 +1,27 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyClass } from '../../../singleton'; -import { LibraryOptions, ResourcesConfig } from '../../../singleton/types'; +import { AmplifyClass } from '~/src/singleton'; +import { LibraryOptions, ResourcesConfig } from '~/src/singleton/types'; -export namespace AmplifyServer { - export type ContextToken = { - readonly value: Symbol; - }; +export declare namespace AmplifyServer { + export interface ContextToken { + readonly value: symbol; + } - export type ContextSpec = { + export interface ContextSpec { readonly token: ContextToken; - }; + } - export type Context = { + export interface Context { amplify: AmplifyClass; - }; - - export interface RunOperationWithContext { - ( - amplifyConfig: ResourcesConfig, - libraryOptions: LibraryOptions, - operation: ( - contextSpec: AmplifyServer.ContextSpec - ) => Result | Promise - ): Promise; } + + export type RunOperationWithContext = ( + amplifyConfig: ResourcesConfig, + libraryOptions: LibraryOptions, + operation: ( + contextSpec: AmplifyServer.ContextSpec, + ) => Result | Promise, + ) => Promise; } diff --git a/packages/core/src/adapterCore/serverContext/types/cookieStorage.ts b/packages/core/src/adapterCore/serverContext/types/cookieStorage.ts index 019a69951d1..6069c749903 100644 --- a/packages/core/src/adapterCore/serverContext/types/cookieStorage.ts +++ b/packages/core/src/adapterCore/serverContext/types/cookieStorage.ts @@ -3,7 +3,7 @@ import { CookieSerializeOptions } from 'cookie'; -export namespace CookieStorage { +export declare namespace CookieStorage { export type SetCookieOptions = Partial< Pick< CookieSerializeOptions, diff --git a/packages/core/src/awsClients/cognitoIdentity/base.ts b/packages/core/src/awsClients/cognitoIdentity/base.ts index 41584c36960..79836abd471 100644 --- a/packages/core/src/awsClients/cognitoIdentity/base.ts +++ b/packages/core/src/awsClients/cognitoIdentity/base.ts @@ -9,17 +9,17 @@ import { HttpResponse, Middleware, getDnsSuffix, - unauthenticatedHandler, parseJsonError, -} from '../../clients'; -import { composeTransferHandler } from '../../clients/internal/composeTransferHandler'; + unauthenticatedHandler, +} from '~/src/clients'; +import { composeTransferHandler } from '~/src/clients/internal/composeTransferHandler'; import { - jitteredBackoff, getRetryDecider, -} from '../../clients/middleware/retry'; -import { getAmplifyUserAgent } from '../../Platform'; -import { observeFrameworkChanges } from '../../Platform/detectFramework'; -import { AmplifyUrl } from '../../utils/amplifyUrl'; + jitteredBackoff, +} from '~/src/clients/middleware/retry'; +import { getAmplifyUserAgent } from '~/src/Platform'; +import { observeFrameworkChanges } from '~/src/Platform/detectFramework'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; /** * The service name used to sign requests if the API requires authentication. @@ -31,19 +31,19 @@ const SERVICE_NAME = 'cognito-identity'; */ const endpointResolver = ({ region }: EndpointResolverOptions) => ({ url: new AmplifyUrl( - `https://cognito-identity.${region}.${getDnsSuffix(region)}` + `https://cognito-identity.${region}.${getDnsSuffix(region)}`, ), }); /** * A Cognito Identity-specific middleware that disables caching for all requests. */ -const disableCacheMiddleware: Middleware = - () => (next, context) => - async function disableCacheMiddleware(request) { - request.headers['cache-control'] = 'no-store'; - return next(request); - }; +const disableCacheMiddleware: Middleware = + () => next => async request => { + request.headers['cache-control'] = 'no-store'; + + return next(request); + }; /** * A Cognito Identity-specific transfer handler that does NOT sign requests, and @@ -88,7 +88,7 @@ export const getSharedHeaders = (operation: string): Headers => ({ export const buildHttpRpcRequest = ( { url }: Endpoint, headers: Headers, - body: any + body: any, ): HttpRequest => ({ headers, url, diff --git a/packages/core/src/awsClients/cognitoIdentity/getCredentialsForIdentity.ts b/packages/core/src/awsClients/cognitoIdentity/getCredentialsForIdentity.ts index cb08e2f17e6..33efc4350c7 100644 --- a/packages/core/src/awsClients/cognitoIdentity/getCredentialsForIdentity.ts +++ b/packages/core/src/awsClients/cognitoIdentity/getCredentialsForIdentity.ts @@ -1,12 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - buildHttpRpcRequest, - cognitoIdentityTransferHandler, - defaultConfig, - getSharedHeaders, -} from './base'; import { Endpoint, HttpRequest, @@ -14,33 +8,43 @@ import { parseJsonBody, parseJsonError, parseMetadata, -} from '../../clients'; -import { composeServiceApi } from '../../clients/internal'; +} from '~/src/clients'; +import { composeServiceApi } from '~/src/clients/internal'; + +import { + buildHttpRpcRequest, + cognitoIdentityTransferHandler, + defaultConfig, + getSharedHeaders, +} from './base'; + import type { + Credentials, GetCredentialsForIdentityCommandInput as GetCredentialsForIdentityInput, GetCredentialsForIdentityCommandOutput as GetCredentialsForIdentityOutput, - Credentials, } from './types'; export type { GetCredentialsForIdentityInput, GetCredentialsForIdentityOutput }; const getCredentialsForIdentitySerializer = ( input: GetCredentialsForIdentityInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const headers = getSharedHeaders('GetCredentialsForIdentity'); const body = JSON.stringify(input); + return buildHttpRpcRequest(endpoint, headers, body); }; const getCredentialsForIdentityDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = await parseJsonError(response); throw error; } else { const body = await parseJsonBody(response); + return { IdentityId: body.IdentityId, Credentials: deserializeCredentials(body.Credentials), @@ -70,5 +74,5 @@ export const getCredentialsForIdentity = composeServiceApi( cognitoIdentityTransferHandler, getCredentialsForIdentitySerializer, getCredentialsForIdentityDeserializer, - defaultConfig + defaultConfig, ); diff --git a/packages/core/src/awsClients/cognitoIdentity/getId.ts b/packages/core/src/awsClients/cognitoIdentity/getId.ts index a5456381e42..23f0c3f2db1 100644 --- a/packages/core/src/awsClients/cognitoIdentity/getId.ts +++ b/packages/core/src/awsClients/cognitoIdentity/getId.ts @@ -1,12 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { - buildHttpRpcRequest, - cognitoIdentityTransferHandler, - defaultConfig, - getSharedHeaders, -} from './base'; import { Endpoint, HttpRequest, @@ -14,8 +8,15 @@ import { parseJsonBody, parseJsonError, parseMetadata, -} from '../../clients'; -import { composeServiceApi } from '../../clients/internal'; +} from '~/src/clients'; +import { composeServiceApi } from '~/src/clients/internal'; + +import { + buildHttpRpcRequest, + cognitoIdentityTransferHandler, + defaultConfig, + getSharedHeaders, +} from './base'; import { GetIdCommandInput as GetIdInput, GetIdCommandOutput as GetIdOutput, @@ -25,21 +26,23 @@ export type { GetIdInput, GetIdOutput }; const getIdSerializer = ( input: GetIdInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const headers = getSharedHeaders('GetId'); const body = JSON.stringify(input); + return buildHttpRpcRequest(endpoint, headers, body); }; const getIdDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = await parseJsonError(response); throw error; } else { const body = await parseJsonBody(response); + return { IdentityId: body.IdentityId, $metadata: parseMetadata(response), @@ -54,5 +57,5 @@ export const getId = composeServiceApi( cognitoIdentityTransferHandler, getIdSerializer, getIdDeserializer, - defaultConfig + defaultConfig, ); diff --git a/packages/core/src/awsClients/cognitoIdentity/types.ts b/packages/core/src/awsClients/cognitoIdentity/types.ts index 6ce1d27ca2a..0a324decc1c 100644 --- a/packages/core/src/awsClients/cognitoIdentity/types.ts +++ b/packages/core/src/awsClients/cognitoIdentity/types.ts @@ -32,8 +32,8 @@ export interface Credentials { * * The input for {@link GetCredentialsForIdentityCommand}. */ -export interface GetCredentialsForIdentityCommandInput - extends GetCredentialsForIdentityInput {} +export type GetCredentialsForIdentityCommandInput = + GetCredentialsForIdentityInput; /** * @public * @@ -91,7 +91,7 @@ export interface GetCredentialsForIdentityResponse { * * The input for {@link GetIdCommand}. */ -export interface GetIdCommandInput extends GetIdInput {} +export type GetIdCommandInput = GetIdInput; /** * @public * diff --git a/packages/core/src/awsClients/pinpoint/base.ts b/packages/core/src/awsClients/pinpoint/base.ts index ffca0f847d6..372cb5aa3c9 100644 --- a/packages/core/src/awsClients/pinpoint/base.ts +++ b/packages/core/src/awsClients/pinpoint/base.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getDnsSuffix } from '../../clients/endpoints'; +import { getDnsSuffix } from '~/src/clients/endpoints'; import { - jitteredBackoff, getRetryDecider, -} from '../../clients/middleware/retry'; -import { parseJsonError } from '../../clients/serde/json'; -import type { EndpointResolverOptions, Headers } from '../../clients/types'; -import { getAmplifyUserAgent } from '../../Platform'; -import { AmplifyUrl } from '../../utils/amplifyUrl'; + jitteredBackoff, +} from '~/src/clients/middleware/retry'; +import { parseJsonError } from '~/src/clients/serde/json'; +import { getAmplifyUserAgent } from '~/src/Platform'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; +import type { EndpointResolverOptions, Headers } from '~/src/clients/types'; /** * The service name used to sign requests if the API requires authentication. diff --git a/packages/core/src/awsClients/pinpoint/errorHelpers.ts b/packages/core/src/awsClients/pinpoint/errorHelpers.ts index eff5f3784d5..fc9a4c004c9 100644 --- a/packages/core/src/awsClients/pinpoint/errorHelpers.ts +++ b/packages/core/src/awsClients/pinpoint/errorHelpers.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { createAssertionFunction } from '../../errors'; -import { AmplifyErrorMap, AssertionFunction } from '../../types'; +import { createAssertionFunction } from '~/src/errors'; +import { AmplifyErrorMap, AssertionFunction } from '~/src/types'; export enum PinpointValidationErrorCode { NoAppId = 'NoAppId', diff --git a/packages/core/src/awsClients/pinpoint/getInAppMessages.ts b/packages/core/src/awsClients/pinpoint/getInAppMessages.ts index 83969ad0f44..9dd6a57d0df 100644 --- a/packages/core/src/awsClients/pinpoint/getInAppMessages.ts +++ b/packages/core/src/awsClients/pinpoint/getInAppMessages.ts @@ -1,44 +1,48 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { authenticatedHandler } from '../../clients/handlers/authenticated'; -import { composeServiceApi } from '../../clients/internal/composeServiceApi'; -import { extendedEncodeURIComponent } from '../../clients/middleware/signing/utils/extendedEncodeURIComponent'; +import { authenticatedHandler } from '~/src/clients/handlers/authenticated'; +import { composeServiceApi } from '~/src/clients/internal/composeServiceApi'; +import { extendedEncodeURIComponent } from '~/src/clients/middleware/signing/utils/extendedEncodeURIComponent'; import { parseJsonBody, parseJsonError, parseMetadata, -} from '../../clients/serde'; -import { Endpoint, HttpRequest, HttpResponse } from '../../clients/types'; +} from '~/src/clients/serde'; +import { Endpoint, HttpRequest, HttpResponse } from '~/src/clients/types'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; + import { defaultConfig, getSharedHeaders } from './base'; + import type { GetInAppMessagesCommandInput as GetInAppMessagesInput, GetInAppMessagesCommandOutput as GetInAppMessagesOutput, } from './types'; -import { AmplifyUrl } from '../../utils/amplifyUrl'; export type { GetInAppMessagesInput, GetInAppMessagesOutput }; const getInAppMessagesSerializer = ( { ApplicationId = '', EndpointId = '' }: GetInAppMessagesInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const headers = getSharedHeaders(); const url = new AmplifyUrl(endpoint.url); url.pathname = `v1/apps/${extendedEncodeURIComponent( - ApplicationId + ApplicationId, )}/endpoints/${extendedEncodeURIComponent(EndpointId)}/inappmessages`; + return { method: 'GET', headers, url }; }; const getInAppMessagesDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = await parseJsonError(response); throw error; } else { const { InAppMessageCampaigns } = await parseJsonBody(response); + return { InAppMessagesResponse: { InAppMessageCampaigns }, $metadata: parseMetadata(response), @@ -53,5 +57,5 @@ export const getInAppMessages = composeServiceApi( authenticatedHandler, getInAppMessagesSerializer, getInAppMessagesDeserializer, - defaultConfig + defaultConfig, ); diff --git a/packages/core/src/awsClients/pinpoint/putEvents.ts b/packages/core/src/awsClients/pinpoint/putEvents.ts index 5a2be6bfcc8..418e3f65cb0 100644 --- a/packages/core/src/awsClients/pinpoint/putEvents.ts +++ b/packages/core/src/awsClients/pinpoint/putEvents.ts @@ -1,45 +1,49 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { authenticatedHandler } from '../../clients/handlers/authenticated'; -import { composeServiceApi } from '../../clients/internal/composeServiceApi'; -import { extendedEncodeURIComponent } from '../../clients/middleware/signing/utils/extendedEncodeURIComponent'; +import { authenticatedHandler } from '~/src/clients/handlers/authenticated'; +import { composeServiceApi } from '~/src/clients/internal/composeServiceApi'; +import { extendedEncodeURIComponent } from '~/src/clients/middleware/signing/utils/extendedEncodeURIComponent'; import { parseJsonBody, parseJsonError, parseMetadata, -} from '../../clients/serde'; -import { Endpoint, HttpRequest, HttpResponse } from '../../clients/types'; +} from '~/src/clients/serde'; +import { Endpoint, HttpRequest, HttpResponse } from '~/src/clients/types'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; + import { defaultConfig, getSharedHeaders } from './base'; -import { assert, PinpointValidationErrorCode } from './errorHelpers'; +import { PinpointValidationErrorCode, assert } from './errorHelpers'; + import type { PutEventsCommandInput as PutEventsInput, PutEventsCommandOutput as PutEventsOutput, } from './types'; -import { AmplifyUrl } from '../../utils/amplifyUrl'; export type { PutEventsInput, PutEventsOutput }; const putEventsSerializer = ( { ApplicationId, EventsRequest }: PutEventsInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { assert(!!ApplicationId, PinpointValidationErrorCode.NoAppId); const headers = getSharedHeaders(); const url = new AmplifyUrl(endpoint.url); url.pathname = `v1/apps/${extendedEncodeURIComponent(ApplicationId)}/events`; const body = JSON.stringify(EventsRequest ?? {}); + return { method: 'POST', headers, url, body }; }; const putEventsDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = await parseJsonError(response); throw error; } else { const { Results } = await parseJsonBody(response); + return { EventsResponse: { Results }, $metadata: parseMetadata(response), @@ -54,5 +58,5 @@ export const putEvents = composeServiceApi( authenticatedHandler, putEventsSerializer, putEventsDeserializer, - defaultConfig + defaultConfig, ); diff --git a/packages/core/src/awsClients/pinpoint/types.ts b/packages/core/src/awsClients/pinpoint/types.ts index efd08fa884c..24bc4469d99 100644 --- a/packages/core/src/awsClients/pinpoint/types.ts +++ b/packages/core/src/awsClients/pinpoint/types.ts @@ -1,4 +1,3 @@ -/* tslint:disable:max-line-length */ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 @@ -377,7 +376,7 @@ export interface EventsResponse { * * The input for {@link GetInAppMessagesCommand}. */ -export interface GetInAppMessagesCommandInput extends GetInAppMessagesRequest {} +export type GetInAppMessagesCommandInput = GetInAppMessagesRequest /** * @public * @@ -691,7 +690,7 @@ export interface PublicEndpoint { * * The input for {@link PutEventsCommand}. */ -export interface PutEventsCommandInput extends PutEventsRequest {} +export type PutEventsCommandInput = PutEventsRequest /** * @public * @@ -777,7 +776,7 @@ export interface SetDimension { * * The input for {@link UpdateEndpointCommand}. */ -export interface UpdateEndpointCommandInput extends UpdateEndpointRequest {} +export type UpdateEndpointCommandInput = UpdateEndpointRequest /** * @public * diff --git a/packages/core/src/awsClients/pinpoint/updateEndpoint.ts b/packages/core/src/awsClients/pinpoint/updateEndpoint.ts index 0676abeaa04..deb0613e73d 100644 --- a/packages/core/src/awsClients/pinpoint/updateEndpoint.ts +++ b/packages/core/src/awsClients/pinpoint/updateEndpoint.ts @@ -1,45 +1,49 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { authenticatedHandler } from '../../clients/handlers/authenticated'; -import { composeServiceApi } from '../../clients/internal/composeServiceApi'; -import { extendedEncodeURIComponent } from '../../clients/middleware/signing/utils/extendedEncodeURIComponent'; +import { authenticatedHandler } from '~/src/clients/handlers/authenticated'; +import { composeServiceApi } from '~/src/clients/internal/composeServiceApi'; +import { extendedEncodeURIComponent } from '~/src/clients/middleware/signing/utils/extendedEncodeURIComponent'; import { parseJsonBody, parseJsonError, parseMetadata, -} from '../../clients/serde'; -import { Endpoint, HttpRequest, HttpResponse } from '../../clients/types'; +} from '~/src/clients/serde'; +import { Endpoint, HttpRequest, HttpResponse } from '~/src/clients/types'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; + import { defaultConfig, getSharedHeaders } from './base'; + import type { UpdateEndpointCommandInput as UpdateEndpointInput, UpdateEndpointCommandOutput as UpdateEndpointOutput, } from './types'; -import { AmplifyUrl } from '../../utils/amplifyUrl'; export type { UpdateEndpointInput, UpdateEndpointOutput }; const updateEndpointSerializer = ( { ApplicationId = '', EndpointId = '', EndpointRequest }: UpdateEndpointInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const headers = getSharedHeaders(); const url = new AmplifyUrl(endpoint.url); url.pathname = `v1/apps/${extendedEncodeURIComponent( - ApplicationId + ApplicationId, )}/endpoints/${extendedEncodeURIComponent(EndpointId)}`; const body = JSON.stringify(EndpointRequest); + return { method: 'PUT', headers, url, body }; }; const updateEndpointDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = await parseJsonError(response); throw error; } else { const { Message, RequestID } = await parseJsonBody(response); + return { MessageBody: { Message, @@ -57,5 +61,5 @@ export const updateEndpoint = composeServiceApi( authenticatedHandler, updateEndpointSerializer, updateEndpointDeserializer, - defaultConfig + defaultConfig, ); diff --git a/packages/core/src/clients/endpoints/getDnsSuffix.ts b/packages/core/src/clients/endpoints/getDnsSuffix.ts index dd5e8ee9729..28f24a00b3b 100644 --- a/packages/core/src/clients/endpoints/getDnsSuffix.ts +++ b/packages/core/src/clients/endpoints/getDnsSuffix.ts @@ -21,5 +21,6 @@ export const getDnsSuffix = (region: string): string => { return outputs.dnsSuffix; } } + return defaultPartition.outputs.dnsSuffix; }; diff --git a/packages/core/src/clients/handlers/authenticated.ts b/packages/core/src/clients/handlers/authenticated.ts index 0260c20ebd9..9abbae372c2 100644 --- a/packages/core/src/clients/handlers/authenticated.ts +++ b/packages/core/src/clients/handlers/authenticated.ts @@ -1,12 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { retryMiddleware, RetryOptions } from '../middleware/retry'; -import { signingMiddleware, SigningOptions } from '../middleware/signing'; -import { userAgentMiddleware, UserAgentOptions } from '../middleware/userAgent'; -import { composeTransferHandler } from '../internal/composeTransferHandler'; +import { RetryOptions, retryMiddleware } from '~/src/clients/middleware/retry'; +import { + SigningOptions, + signingMiddleware, +} from '~/src/clients/middleware/signing'; +import { + UserAgentOptions, + userAgentMiddleware, +} from '~/src/clients/middleware/userAgent'; +import { composeTransferHandler } from '~/src/clients/internal/composeTransferHandler'; +import { HttpRequest, HttpResponse } from '~/src/clients/types'; + import { fetchTransferHandler } from './fetch'; -import { HttpRequest, HttpResponse } from '../types'; export const authenticatedHandler = composeTransferHandler< [UserAgentOptions, RetryOptions, SigningOptions], diff --git a/packages/core/src/clients/handlers/fetch.ts b/packages/core/src/clients/handlers/fetch.ts index 5aeabad53c0..1bf388249e4 100644 --- a/packages/core/src/clients/handlers/fetch.ts +++ b/packages/core/src/clients/handlers/fetch.ts @@ -1,9 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest, HttpResponse, HttpTransferOptions } from '../types/http'; -import { TransferHandler } from '../types/core'; -import { withMemoization } from '../utils/memoization'; +import { + HttpRequest, + HttpResponse, + HttpTransferOptions, +} from '~/src/clients/types/http'; +import { TransferHandler } from '~/src/clients/types/core'; +import { withMemoization } from '~/src/clients/utils/memoization'; const shouldSendBody = (method: string) => !['HEAD', 'GET', 'DELETE'].includes(method.toUpperCase()); @@ -15,7 +19,7 @@ export const fetchTransferHandler: TransferHandler< HttpTransferOptions > = async ( { url, method, headers, body }, - { abortSignal, cache, withCrossDomainCredentials } + { abortSignal, cache, withCrossDomainCredentials }, ) => { let resp: Response; try { diff --git a/packages/core/src/clients/handlers/unauthenticated.ts b/packages/core/src/clients/handlers/unauthenticated.ts index 047d7afcb01..054900aa9f6 100644 --- a/packages/core/src/clients/handlers/unauthenticated.ts +++ b/packages/core/src/clients/handlers/unauthenticated.ts @@ -1,11 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { retryMiddleware, RetryOptions } from '../middleware/retry'; -import { userAgentMiddleware, UserAgentOptions } from '../middleware/userAgent'; -import { composeTransferHandler } from '../internal/composeTransferHandler'; +import { RetryOptions, retryMiddleware } from '~/src/clients/middleware/retry'; +import { + UserAgentOptions, + userAgentMiddleware, +} from '~/src/clients/middleware/userAgent'; +import { composeTransferHandler } from '~/src/clients/internal/composeTransferHandler'; +import { HttpRequest, HttpResponse } from '~/src/clients/types'; + import { fetchTransferHandler } from './fetch'; -import { HttpRequest, HttpResponse } from '../types'; export const unauthenticatedHandler = composeTransferHandler< [UserAgentOptions, RetryOptions], diff --git a/packages/core/src/clients/internal/composeServiceApi.ts b/packages/core/src/clients/internal/composeServiceApi.ts index 650c59fb1e5..654a81819c4 100644 --- a/packages/core/src/clients/internal/composeServiceApi.ts +++ b/packages/core/src/clients/internal/composeServiceApi.ts @@ -1,9 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ServiceClientOptions } from '../types/aws'; -import { TransferHandler, Endpoint } from '../types/core'; -import { HttpRequest, HttpResponse } from '../types/http'; +import { ServiceClientOptions } from '~/src/clients/types/aws'; +import { Endpoint, TransferHandler } from '~/src/clients/types/core'; +import { HttpRequest, HttpResponse } from '~/src/clients/types/http'; export const composeServiceApi = < TransferHandlerOptions, @@ -18,10 +18,10 @@ export const composeServiceApi = < >, serializer: ( input: Input, - endpoint: Endpoint + endpoint: Endpoint, ) => Promise | HttpRequest, deserializer: (output: HttpResponse) => Promise, - defaultConfig: Partial + defaultConfig: Partial, ) => { return async ( config: OptionalizeKey< @@ -31,7 +31,7 @@ export const composeServiceApi = < InferEndpointResolverOptionType, DefaultConfig >, - input: Input + input: Input, ) => { const resolvedConfig = { ...defaultConfig, @@ -41,7 +41,7 @@ export const composeServiceApi = < // Currently S3 supports additional `useAccelerateEndpoint` option to use accelerate endpoint. const endpoint = await resolvedConfig.endpointResolver( resolvedConfig, - input + input, ); // Unlike AWS SDK clients, a serializer should NOT populate the `host` or `content-length` headers. // Both of these headers are prohibited per Spec(https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name). @@ -50,7 +50,8 @@ export const composeServiceApi = < const response = await transferHandler(request, { ...resolvedConfig, }); - return await deserializer(response); + + return deserializer(response); }; }; @@ -66,7 +67,7 @@ type OptionalizeKey = { }; type InferEndpointResolverOptionType = T extends { - endpointResolver: (options: infer EndpointOptions) => any; + endpointResolver(options: infer EndpointOptions): any; } ? EndpointOptions : never; diff --git a/packages/core/src/clients/internal/composeTransferHandler.ts b/packages/core/src/clients/internal/composeTransferHandler.ts index edb0137c1bd..9a57ff1f1fc 100644 --- a/packages/core/src/clients/internal/composeTransferHandler.ts +++ b/packages/core/src/clients/internal/composeTransferHandler.ts @@ -4,10 +4,10 @@ import { Middleware, MiddlewareHandler, - TransferHandler, Request as RequestBase, Response as ResponseBase, -} from '../types'; + TransferHandler, +} from '~/src/clients/types'; /** * Compose a transfer handler with a core transfer handler and a list of middleware. @@ -26,26 +26,27 @@ export const composeTransferHandler = Request, Response, any - > = TransferHandler, + > = TransferHandler, >( coreHandler: CoreHandler, - middleware: OptionToMiddleware + middleware: OptionToMiddleware, ) => ( request: Request, options: MergeNoConflictKeys< [...MiddlewareOptionsArr, InferOptionTypeFromTransferHandler] - > + >, ) => { const context = {}; let composedHandler: MiddlewareHandler = ( - request: Request - ) => coreHandler(request, options); + composedHandlerRequest: Request, + ) => coreHandler(composedHandlerRequest, options); for (let i = middleware.length - 1; i >= 0; i--) { const m = middleware[i]; const resolvedMiddleware = m(options); composedHandler = resolvedMiddleware(composedHandler, context); } + return composedHandler(request); }; diff --git a/packages/core/src/clients/middleware/retry/defaultRetryDecider.ts b/packages/core/src/clients/middleware/retry/defaultRetryDecider.ts index 4ee77f40366..bd97f5f87ed 100644 --- a/packages/core/src/clients/middleware/retry/defaultRetryDecider.ts +++ b/packages/core/src/clients/middleware/retry/defaultRetryDecider.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpResponse, ErrorParser } from '../../types'; +import { ErrorParser, HttpResponse } from '~/src/clients/types'; + import { isClockSkewError } from './isClockSkewError'; /** @@ -15,8 +16,9 @@ export const getRetryDecider = (error as Error & { code: string }) ?? (await errorParser(response)) ?? undefined; - const errorCode = parsedError?.['code']; + const errorCode = parsedError?.code; const statusCode = response?.statusCode; + return ( isConnectionError(error) || isThrottlingError(statusCode, errorCode) || @@ -53,7 +55,7 @@ const isThrottlingError = (statusCode?: number, errorCode?: string) => (!!errorCode && THROTTLING_ERROR_CODES.includes(errorCode)); const isConnectionError = (error?: unknown) => - (error as Error)?.['name'] === 'Network error'; + (error as Error)?.name === 'Network error'; const isServerSideError = (statusCode?: number, errorCode?: string) => (!!statusCode && [500, 502, 503, 504].includes(statusCode)) || diff --git a/packages/core/src/clients/middleware/retry/jitteredBackoff.ts b/packages/core/src/clients/middleware/retry/jitteredBackoff.ts index 37ecbd58858..211ef9bc2e6 100644 --- a/packages/core/src/clients/middleware/retry/jitteredBackoff.ts +++ b/packages/core/src/clients/middleware/retry/jitteredBackoff.ts @@ -1,15 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { jitteredBackoff as jitteredBackoffUtil } from '~/src/utils'; + import type { RetryOptions } from './middleware'; // TODO: [v6] The separate retry utility is used by Data packages now and will replaced by retry middleware. -import { jitteredBackoff as jitteredBackoffUtil } from '../../../utils'; const DEFAULT_MAX_DELAY_MS = 5 * 60 * 1000; export const jitteredBackoff: RetryOptions['computeDelay'] = attempt => { const delayFunction = jitteredBackoffUtil(DEFAULT_MAX_DELAY_MS); const delay = delayFunction(attempt); + // The delayFunction returns false when the delay is greater than the max delay(5 mins). // In this case, the retry middleware will delay 5 mins instead, as a ceiling of the delay. return delay === false ? DEFAULT_MAX_DELAY_MS : delay; diff --git a/packages/core/src/clients/middleware/retry/middleware.ts b/packages/core/src/clients/middleware/retry/middleware.ts index 9a00dd270c7..726c2899685 100644 --- a/packages/core/src/clients/middleware/retry/middleware.ts +++ b/packages/core/src/clients/middleware/retry/middleware.ts @@ -6,7 +6,7 @@ import { MiddlewareHandler, Request, Response, -} from '../../types/core'; +} from '~/src/clients/types/core'; const DEFAULT_RETRY_ATTEMPTS = 3; @@ -21,14 +21,14 @@ export interface RetryOptions { * @param error Optional error thrown from previous attempts. * @returns True if the request should be retried. */ - retryDecider: (response?: TResponse, error?: unknown) => Promise; + retryDecider(response?: TResponse, error?: unknown): Promise; /** * Function to compute the delay in milliseconds before the next retry based * on the number of attempts. * @param attempt Current number of attempts, including the first attempt. * @returns Delay in milliseconds. */ - computeDelay: (attempt: number) => number; + computeDelay(attempt: number): number; /** * Maximum number of retry attempts, starting from 1. Defaults to 3. */ @@ -51,11 +51,12 @@ export const retryMiddleware = ({ if (maxAttempts < 1) { throw new Error('maxAttempts must be greater than 0'); } + return ( - next: MiddlewareHandler, - context: MiddlewareContext - ) => - async function retryMiddleware(request: TInput) { + next: MiddlewareHandler, + context: MiddlewareContext, + ) => + async (request: TInput) => { let error: unknown; let attemptsCount: number = context.attemptsCount ?? 0; let response: TOutput | undefined; @@ -64,6 +65,7 @@ export const retryMiddleware = ({ const handleTerminalErrorOrResponse = () => { if (response) { addOrIncrementMetadataAttempts(response, attemptsCount); + return response; } else { addOrIncrementMetadataAttempts(error as object, attemptsCount); @@ -110,28 +112,29 @@ const cancellableSleep = (timeoutMs: number, abortSignal?: AbortSignal) => { return Promise.resolve(); } let timeoutId: ReturnType; - let sleepPromiseResolveFn: Function; + let sleepPromiseResolveFn: () => void; const sleepPromise = new Promise(resolve => { sleepPromiseResolveFn = resolve; timeoutId = setTimeout(resolve, timeoutMs); }); - abortSignal?.addEventListener('abort', function cancelSleep(event) { + abortSignal?.addEventListener('abort', function cancelSleep() { clearTimeout(timeoutId); abortSignal?.removeEventListener('abort', cancelSleep); sleepPromiseResolveFn(); }); + return sleepPromise; }; const addOrIncrementMetadataAttempts = ( nextHandlerOutput: Record, - attempts: number + attempts: number, ) => { if (Object.prototype.toString.call(nextHandlerOutput) !== '[object Object]') { return; } - nextHandlerOutput['$metadata'] = { - ...(nextHandlerOutput['$metadata'] ?? {}), + nextHandlerOutput.$metadata = { + ...(nextHandlerOutput.$metadata ?? {}), attempts, }; }; diff --git a/packages/core/src/clients/middleware/signing/middleware.ts b/packages/core/src/clients/middleware/signing/middleware.ts index 143c1daac5e..4c0f2d43312 100644 --- a/packages/core/src/clients/middleware/signing/middleware.ts +++ b/packages/core/src/clients/middleware/signing/middleware.ts @@ -6,7 +6,8 @@ import { HttpRequest, HttpResponse, MiddlewareHandler, -} from '../../types'; +} from '~/src/clients/types'; + import { signRequest } from './signer/signatureV4'; import { getSkewCorrectedDate } from './utils/getSkewCorrectedDate'; import { getUpdatedSystemClockOffset } from './utils/getUpdatedSystemClockOffset'; @@ -39,8 +40,9 @@ export const signingMiddleware = ({ uriEscapePath = true, }: SigningOptions) => { let currentSystemClockOffset: number; + return (next: MiddlewareHandler) => - async function signingMiddleware(request: HttpRequest) { + async (request: HttpRequest) => { currentSystemClockOffset = currentSystemClockOffset ?? 0; const signRequestOptions = { credentials: @@ -59,9 +61,10 @@ export const signingMiddleware = ({ if (dateString) { currentSystemClockOffset = getUpdatedSystemClockOffset( Date.parse(dateString), - currentSystemClockOffset + currentSystemClockOffset, ); } + return response; }; }; diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/presignUrl.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/presignUrl.ts index 0b459e24ebe..a4fb984e2f6 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/presignUrl.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/presignUrl.ts @@ -1,7 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Presignable, PresignUrlOptions } from './types'; +import { AmplifyUrl } from '~/src/utils/amplifyUrl'; + +import { PresignUrlOptions, Presignable } from './types'; import { ALGORITHM_QUERY_PARAM, AMZ_DATE_QUERY_PARAM, @@ -15,7 +17,6 @@ import { } from './constants'; import { getSigningValues } from './utils/getSigningValues'; import { getSignature } from './utils/getSignature'; -import { AmplifyUrl } from '../../../../../utils/amplifyUrl'; /** * Given a `Presignable` object, returns a Signature Version 4 presigned `URL` object. @@ -26,14 +27,13 @@ import { AmplifyUrl } from '../../../../../utils/amplifyUrl'; */ export const presignUrl = ( { body, method = 'GET', url }: Presignable, - { expiration, ...options }: PresignUrlOptions + { expiration, ...options }: PresignUrlOptions, ): URL => { const signingValues = getSigningValues(options); const { accessKeyId, credentialScope, longDate, sessionToken } = signingValues; // create the request to sign - // @ts-ignore URL constructor accepts a URL object const presignedUrl = new AmplifyUrl(url); Object.entries({ [ALGORITHM_QUERY_PARAM]: SHA256_ALGORITHM_IDENTIFIER, diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/signRequest.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/signRequest.ts index 4e19be6b431..3101c2d9e8e 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/signRequest.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/signRequest.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest } from '../../../../types'; +import { HttpRequest } from '~/src/clients/types'; + import { SignRequestOptions } from './types/signer'; import { getSignedHeaders } from './utils/getSignedHeaders'; import { getSigningValues } from './utils/getSigningValues'; @@ -23,7 +24,7 @@ import { getSignature } from './utils/getSignature'; */ export const signRequest = ( request: HttpRequest, - options: SignRequestOptions + options: SignRequestOptions, ): HttpRequest => { const signingValues = getSigningValues(options); const { accessKeyId, credentialScope, longDate, sessionToken } = diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/types/signer.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/types/signer.ts index 24638305c92..5b89b382a51 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/types/signer.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/types/signer.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Credentials, HttpRequest } from '../../../../../types'; +import { Credentials, HttpRequest } from '~/src/clients/types'; export interface SignRequestOptions { credentials: Credentials; diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/dataHashHelpers.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/dataHashHelpers.ts index 017ba8a5c9d..1e00f3e70cd 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/dataHashHelpers.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/dataHashHelpers.ts @@ -15,12 +15,13 @@ import { toHex } from '@smithy/util-hex-encoding'; */ export const getHashedData = ( key: SourceData | null, - data: SourceData + data: SourceData, ): Uint8Array => { const sha256 = new Sha256(key ?? undefined); sha256.update(data); // TODO: V6 flip to async digest const hashedData = sha256.digestSync(); + return hashedData; }; @@ -35,8 +36,9 @@ export const getHashedData = ( */ export const getHashedDataAsHex = ( key: SourceData | null, - data: SourceData + data: SourceData, ): string => { const hashedData = getHashedData(key, data); + return toHex(hashedData); }; diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalHeaders.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalHeaders.ts index 1f9980208cf..1a3ffe8a982 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalHeaders.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalHeaders.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest } from '../../../../../types'; +import { HttpRequest } from '~/src/clients/types'; /** * Returns canonical headers. diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalQueryString.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalQueryString.ts index 1686eaf537c..2c03d84a033 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalQueryString.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalQueryString.ts @@ -13,13 +13,14 @@ * @internal */ export const getCanonicalQueryString = ( - searchParams: URLSearchParams + searchParams: URLSearchParams, ): string => Array.from(searchParams) .sort(([keyA, valA], [keyB, valB]) => { if (keyA === keyB) { return valA < valB ? -1 : 1; } + return keyA < keyB ? -1 : 1; }) .map(([key, val]) => `${escapeUri(key)}=${escapeUri(val)}`) diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalRequest.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalRequest.ts index 52daa8e0def..c8d5ad89a3d 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalRequest.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalRequest.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest } from '../../../../../types'; +import { HttpRequest } from '~/src/clients/types'; + import { getCanonicalHeaders } from './getCanonicalHeaders'; import { getCanonicalQueryString } from './getCanonicalQueryString'; import { getCanonicalUri } from './getCanonicalUri'; @@ -26,7 +27,7 @@ import { getSignedHeaders } from './getSignedHeaders'; */ export const getCanonicalRequest = ( { body, headers, method, url }: HttpRequest, - uriEscapePath = true + uriEscapePath = true, ): string => [ method, diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalUri.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalUri.ts index 67778b40d04..22055b8598d 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalUri.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCanonicalUri.ts @@ -14,7 +14,7 @@ */ export const getCanonicalUri = ( pathname: string, - uriEscapePath = true + uriEscapePath = true, ): string => pathname ? uriEscapePath diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCredentialScope.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCredentialScope.ts index 7e7f94f2b45..1364624f6e2 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCredentialScope.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getCredentialScope.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KEY_TYPE_IDENTIFIER } from '../constants'; +import { KEY_TYPE_IDENTIFIER } from '~/src/clients/middleware/signing/signer/signatureV4/constants'; /** * Returns the credential scope which restricts the resulting signature to the specified region and service. @@ -17,5 +17,5 @@ import { KEY_TYPE_IDENTIFIER } from '../constants'; export const getCredentialScope = ( date: string, region: string, - service: string + service: string, ): string => `${date}/${region}/${service}/${KEY_TYPE_IDENTIFIER}`; diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getFormattedDates.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getFormattedDates.ts index 2b742910c83..2670c67ad26 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getFormattedDates.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getFormattedDates.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { FormattedDates } from '../types/signer'; +import { FormattedDates } from '~/src/clients/middleware/signing/signer/signatureV4/types/signer'; /** * Returns expected date strings to be used in signing. @@ -14,7 +14,8 @@ import { FormattedDates } from '../types/signer'; * @internal */ export const getFormattedDates = (date: Date): FormattedDates => { - const longDate = date.toISOString().replace(/[:\-]|\.\d{3}/g, ''); + const longDate = date.toISOString().replace(/[:-]|\.\d{3}/g, ''); + return { longDate, shortDate: longDate.slice(0, 8), diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getHashedPayload.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getHashedPayload.ts index f2543e78272..6671ac5a7cb 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getHashedPayload.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getHashedPayload.ts @@ -2,8 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { SourceData } from '@aws-sdk/types'; -import { HttpRequest } from '../../../../../types'; -import { EMPTY_HASH, UNSIGNED_PAYLOAD } from '../constants'; +import { HttpRequest } from '~/src/clients/types'; +import { + EMPTY_HASH, + UNSIGNED_PAYLOAD, +} from '~/src/clients/middleware/signing/signer/signatureV4/constants'; + import { getHashedDataAsHex } from './dataHashHelpers'; /** @@ -23,6 +27,7 @@ export const getHashedPayload = (body: HttpRequest['body']): string => { if (isSourceData(body)) { const hashedData = getHashedDataAsHex(null, body); + return hashedData; } diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignature.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignature.ts index 1e079a984bd..e7e50ef02a3 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignature.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignature.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest } from '../../../../../types'; -import { SigningValues } from '../types/signer'; +import { HttpRequest } from '~/src/clients/types'; +import { SigningValues } from '~/src/clients/middleware/signing/signer/signatureV4/types/signer'; + import { getHashedDataAsHex } from './dataHashHelpers'; import { getCanonicalRequest } from './getCanonicalRequest'; import { getSigningKey } from './getSigningKey'; @@ -28,7 +29,7 @@ export const getSignature = ( signingRegion, signingService, uriEscapePath, - }: SigningValues + }: SigningValues, ): string => { // step 1: create a canonical request const canonicalRequest = getCanonicalRequest(request, uriEscapePath); @@ -40,13 +41,13 @@ export const getSignature = ( const stringToSign = getStringToSign( longDate, credentialScope, - hashedRequest + hashedRequest, ); // step 4: calculate the signature const signature = getHashedDataAsHex( getSigningKey(secretAccessKey, shortDate, signingRegion, signingService), - stringToSign + stringToSign, ); return signature; diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignedHeaders.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignedHeaders.ts index a96bbe57868..14f9c88ff24 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignedHeaders.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSignedHeaders.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest } from '../../../../../types'; +import { HttpRequest } from '~/src/clients/types'; /** * Returns signed headers. diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningKey.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningKey.ts index f744db91300..9b20003c05b 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningKey.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningKey.ts @@ -1,7 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { KEY_TYPE_IDENTIFIER, SIGNATURE_IDENTIFIER } from '../constants'; +import { + KEY_TYPE_IDENTIFIER, + SIGNATURE_IDENTIFIER, +} from '~/src/clients/middleware/signing/signer/signatureV4/constants'; + import { getHashedData } from './dataHashHelpers'; /** @@ -20,12 +24,13 @@ export const getSigningKey = ( secretAccessKey: string, date: string, region: string, - service: string + service: string, ): Uint8Array => { const key = `${SIGNATURE_IDENTIFIER}${secretAccessKey}`; const dateKey = getHashedData(key, date); const regionKey = getHashedData(dateKey, region); const serviceKey = getHashedData(regionKey, service); const signingKey = getHashedData(serviceKey, KEY_TYPE_IDENTIFIER); + return signingKey; }; diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningValues.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningValues.ts index 6a2d947e551..d6901bf5a7d 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningValues.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getSigningValues.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SignRequestOptions } from '../types'; -import { SigningValues } from '../types/signer'; +import { SignRequestOptions } from '~/src/clients/middleware/signing/signer/signatureV4/types'; +import { SigningValues } from '~/src/clients/middleware/signing/signer/signatureV4/types/signer'; + import { getCredentialScope } from './getCredentialScope'; import { getFormattedDates } from './getFormattedDates'; @@ -29,8 +30,9 @@ export const getSigningValues = ({ const credentialScope = getCredentialScope( shortDate, signingRegion, - signingService + signingService, ); + return { accessKeyId, credentialScope, diff --git a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getStringToSign.ts b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getStringToSign.ts index 095b79f7065..2722d3c286f 100644 --- a/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getStringToSign.ts +++ b/packages/core/src/clients/middleware/signing/signer/signatureV4/utils/getStringToSign.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SHA256_ALGORITHM_IDENTIFIER } from '../constants'; +import { SHA256_ALGORITHM_IDENTIFIER } from '~/src/clients/middleware/signing/signer/signatureV4/constants'; /** * Returns a string to be signed. @@ -21,8 +21,8 @@ import { SHA256_ALGORITHM_IDENTIFIER } from '../constants'; export const getStringToSign = ( date: string, credentialScope: string, - hashedRequest: string + hashedRequest: string, ): string => [SHA256_ALGORITHM_IDENTIFIER, date, credentialScope, hashedRequest].join( - '\n' + '\n', ); diff --git a/packages/core/src/clients/middleware/signing/utils/getUpdatedSystemClockOffset.ts b/packages/core/src/clients/middleware/signing/utils/getUpdatedSystemClockOffset.ts index d6e24d45474..ed9c66a7cb0 100644 --- a/packages/core/src/clients/middleware/signing/utils/getUpdatedSystemClockOffset.ts +++ b/packages/core/src/clients/middleware/signing/utils/getUpdatedSystemClockOffset.ts @@ -13,10 +13,11 @@ import { isClockSkewed } from './isClockSkewed'; */ export const getUpdatedSystemClockOffset = ( clockTimeInMilliseconds: number, - currentSystemClockOffset: number + currentSystemClockOffset: number, ): number => { if (isClockSkewed(clockTimeInMilliseconds, currentSystemClockOffset)) { return clockTimeInMilliseconds - Date.now(); } + return currentSystemClockOffset; }; diff --git a/packages/core/src/clients/middleware/signing/utils/isClockSkewed.ts b/packages/core/src/clients/middleware/signing/utils/isClockSkewed.ts index da796cb0a56..3b1a1e4d762 100644 --- a/packages/core/src/clients/middleware/signing/utils/isClockSkewed.ts +++ b/packages/core/src/clients/middleware/signing/utils/isClockSkewed.ts @@ -18,9 +18,9 @@ const SKEW_WINDOW = 5 * 60 * 1000; */ export const isClockSkewed = ( clockTimeInMilliseconds: number, - clockOffsetInMilliseconds: number + clockOffsetInMilliseconds: number, ): boolean => Math.abs( getSkewCorrectedDate(clockOffsetInMilliseconds).getTime() - - clockTimeInMilliseconds + clockTimeInMilliseconds, ) >= SKEW_WINDOW; diff --git a/packages/core/src/clients/middleware/userAgent/middleware.ts b/packages/core/src/clients/middleware/userAgent/middleware.ts index 794d94d8e6c..8340f8445cb 100644 --- a/packages/core/src/clients/middleware/userAgent/middleware.ts +++ b/packages/core/src/clients/middleware/userAgent/middleware.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { HttpRequest, HttpResponse } from '../../types/http'; -import { Middleware } from '../../types/core'; +import { HttpRequest, HttpResponse } from '~/src/clients/types/http'; +import { Middleware } from '~/src/clients/types/core'; export interface UserAgentOptions { userAgentHeader?: string; @@ -25,9 +25,10 @@ export const userAgentMiddleware: Middleware< userAgentValue = '', }: UserAgentOptions) => next => { - return async function userAgentMiddleware(request) { + return async request => { if (userAgentValue.trim().length === 0) { const result = await next(request); + return result; } else { const headerName = userAgentHeader.toLowerCase(); @@ -35,6 +36,7 @@ export const userAgentMiddleware: Middleware< ? `${request.headers[headerName]} ${userAgentValue}` : userAgentValue; const response = await next(request); + return response; } }; diff --git a/packages/core/src/clients/serde/json.ts b/packages/core/src/clients/serde/json.ts index c9bbae3c52b..0c33b1c828b 100644 --- a/packages/core/src/clients/serde/json.ts +++ b/packages/core/src/clients/serde/json.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorParser, HttpResponse } from '../types'; +import { ErrorParser, HttpResponse } from '~/src/clients/types'; + import { parseMetadata } from './responseInfo'; /** @@ -17,20 +18,22 @@ export const parseJsonError: ErrorParser = async (response?: HttpResponse) => { } const body = await parseJsonBody(response); const sanitizeErrorCode = (rawValue: string | number): string => { - const [cleanValue] = rawValue.toString().split(/[\,\:]+/); + const [cleanValue] = rawValue.toString().split(/[,:]+/); if (cleanValue.includes('#')) { return cleanValue.split('#')[1]; } + return cleanValue; }; const code = sanitizeErrorCode( response.headers['x-amzn-errortype'] ?? body.code ?? body.__type ?? - 'UnknownError' + 'UnknownError', ); const message = body.message ?? body.Message ?? 'Unknown error'; const error = new Error(message); + return Object.assign(error, { name: code, $metadata: parseMetadata(response), @@ -45,6 +48,7 @@ export const parseJsonBody = async (response: HttpResponse): Promise => { throw new Error('Missing response payload'); } const output = await response.body.json(); + return Object.assign(output, { $metadata: parseMetadata(response), }); diff --git a/packages/core/src/clients/serde/responseInfo.ts b/packages/core/src/clients/serde/responseInfo.ts index f15a5d8685b..226e501bcd2 100644 --- a/packages/core/src/clients/serde/responseInfo.ts +++ b/packages/core/src/clients/serde/responseInfo.ts @@ -1,11 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ResponseMetadata, MetadataBearer } from '@aws-sdk/types'; -import { HttpResponse } from '../types/http'; +import { MetadataBearer, ResponseMetadata } from '@aws-sdk/types'; +import { HttpResponse } from '~/src/clients/types/http'; export const parseMetadata = (response: HttpResponse): ResponseMetadata => { const { headers, statusCode } = response; + return { ...(isMetadataBearer(response) ? response.$metadata : {}), httpStatusCode: statusCode, @@ -18,6 +19,8 @@ export const parseMetadata = (response: HttpResponse): ResponseMetadata => { }; }; -type IsResponse = { $metadata: any }; +interface IsResponse { + $metadata: any; +} const isMetadataBearer = (response: unknown): response is MetadataBearer => typeof (response as IsResponse)?.$metadata === 'object'; diff --git a/packages/core/src/clients/types/aws.ts b/packages/core/src/clients/types/aws.ts index 778f136b906..c9594966d0b 100644 --- a/packages/core/src/clients/types/aws.ts +++ b/packages/core/src/clients/types/aws.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { MetadataBearer } from '@aws-sdk/types'; + import { Endpoint } from './core'; import { HttpResponse } from './http'; @@ -15,11 +16,11 @@ export type SourceData = string | ArrayBuffer | ArrayBufferView; /** * Basic option type for endpoint resolvers. It contains region only. */ -export type EndpointResolverOptions = { region: string }; +export interface EndpointResolverOptions { region: string } export interface ServiceClientOptions { region: string; - endpointResolver: (options: EndpointResolverOptions, input?: any) => Endpoint; + endpointResolver(options: EndpointResolverOptions, input?: any): Endpoint; } /** diff --git a/packages/core/src/clients/types/core.ts b/packages/core/src/clients/types/core.ts index c510b2eed73..cacbab9c068 100644 --- a/packages/core/src/clients/types/core.ts +++ b/packages/core/src/clients/types/core.ts @@ -13,13 +13,11 @@ export interface Response { body: unknown; } -export interface TransferHandler< +export type TransferHandler< Input extends Request, Output extends Response, TransferOptions, -> { - (request: Input, options: TransferOptions): Promise; -} +> = (request: Input, options: TransferOptions) => Promise; /** * A slimmed down version of the AWS SDK v3 middleware handler, only handling instantiated requests @@ -31,12 +29,12 @@ export type MiddlewareHandler = ( /** * The context object to store states across the middleware chain. */ -export type MiddlewareContext = { +export interface MiddlewareContext { /** * The number of times the request has been attempted. This is set by retry middleware */ attemptsCount?: number; -}; +} type ConfiguredMiddleware = ( next: MiddlewareHandler, diff --git a/packages/core/src/clients/types/http.ts b/packages/core/src/clients/types/http.ts index a4c65e070e3..e1b29c42be8 100644 --- a/packages/core/src/clients/types/http.ts +++ b/packages/core/src/clients/types/http.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import type { fetchTransferHandler } from '~/src/clients/handlers/fetch'; + import { Request, Response, TransferHandler } from './core'; -import type { fetchTransferHandler } from '../handlers/fetch'; /** * Use basic Record interface to workaround fetch Header class not available in Node.js diff --git a/packages/core/src/clients/utils/memoization.ts b/packages/core/src/clients/utils/memoization.ts index 08751265ca6..c743208fe8d 100644 --- a/packages/core/src/clients/utils/memoization.ts +++ b/packages/core/src/clients/utils/memoization.ts @@ -11,6 +11,7 @@ */ export const withMemoization = (payloadAccessor: () => Promise) => { let cached: Promise; + return () => { if (!cached) { // Explicitly not awaiting. Intermediate await would add overhead and @@ -18,6 +19,7 @@ export const withMemoization = (payloadAccessor: () => Promise) => { // again before the first `payloadAccessor` call resolves. cached = payloadAccessor(); } + return cached; }; }; diff --git a/packages/core/src/errors/AmplifyError.ts b/packages/core/src/errors/AmplifyError.ts index 9d1c6759bf1..14524256c9f 100644 --- a/packages/core/src/errors/AmplifyError.ts +++ b/packages/core/src/errors/AmplifyError.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyErrorParams } from '../types/errors'; +import { AmplifyErrorParams } from '~/src/types/errors'; export class AmplifyError extends Error { underlyingError?: Error | unknown; diff --git a/packages/core/src/errors/PlatformNotSupportedError.ts b/packages/core/src/errors/PlatformNotSupportedError.ts index 35fe9eda689..2956c956fc1 100644 --- a/packages/core/src/errors/PlatformNotSupportedError.ts +++ b/packages/core/src/errors/PlatformNotSupportedError.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyErrorCode } from '../types'; +import { AmplifyErrorCode } from '~/src/types'; + import { AmplifyError } from './AmplifyError'; export class PlatformNotSupportedError extends AmplifyError { diff --git a/packages/core/src/errors/createAssertionFunction.ts b/packages/core/src/errors/createAssertionFunction.ts index 1e2e7f0b8aa..ecbf0984062 100644 --- a/packages/core/src/errors/createAssertionFunction.ts +++ b/packages/core/src/errors/createAssertionFunction.ts @@ -1,13 +1,14 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyErrorMap, AssertionFunction } from '../types'; +import { AmplifyErrorMap, AssertionFunction } from '~/src/types'; + import { AmplifyError } from './AmplifyError'; export const createAssertionFunction = ( errorMap: AmplifyErrorMap, - AssertionError = AmplifyError + AssertionError = AmplifyError, ): AssertionFunction => (assertion, name, additionalContext) => { const { message, recoverySuggestion } = errorMap[name]; diff --git a/packages/core/src/errors/errorHelpers.ts b/packages/core/src/errors/errorHelpers.ts index 450c4aebba4..ed0cfc9f726 100644 --- a/packages/core/src/errors/errorHelpers.ts +++ b/packages/core/src/errors/errorHelpers.ts @@ -1,8 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { + AmplifyErrorCode, + AmplifyErrorMap, + AssertionFunction, +} from '~/src/types'; + import { createAssertionFunction } from './createAssertionFunction'; -import { AmplifyErrorCode, AmplifyErrorMap, AssertionFunction } from '../types'; const amplifyErrorMap: AmplifyErrorMap = { [AmplifyErrorCode.NoEndpointId]: { diff --git a/packages/core/src/parseAWSExports.ts b/packages/core/src/parseAWSExports.ts index dcb466ddbbb..8f22278f481 100644 --- a/packages/core/src/parseAWSExports.ts +++ b/packages/core/src/parseAWSExports.ts @@ -31,7 +31,7 @@ const authTypeMapping: Record = { */ export const parseAWSExports = ( - config: Record = {} + config: Record = {}, ): ResourcesConfig => { if (!Object.prototype.hasOwnProperty.call(config, 'aws_project_region')) { throw new AmplifyError({ @@ -123,7 +123,7 @@ export const parseAWSExports = ( const defaultAuthMode = authTypeMapping[aws_appsync_authenticationType]; if (!defaultAuthMode) { logger.debug( - `Invalid authentication type ${aws_appsync_authenticationType}. Falling back to IAM.` + `Invalid authentication type ${aws_appsync_authenticationType}. Falling back to IAM.`, ); } amplifyConfig.API = { @@ -155,19 +155,19 @@ export const parseAWSExports = ( aws_cognito_password_protection_settings.passwordPolicyMinLength, requireLowercase: aws_cognito_password_protection_settings.passwordPolicyCharacters?.includes( - 'REQUIRES_LOWERCASE' + 'REQUIRES_LOWERCASE', ) ?? false, requireUppercase: aws_cognito_password_protection_settings.passwordPolicyCharacters?.includes( - 'REQUIRES_UPPERCASE' + 'REQUIRES_UPPERCASE', ) ?? false, requireNumbers: aws_cognito_password_protection_settings.passwordPolicyCharacters?.includes( - 'REQUIRES_NUMBERS' + 'REQUIRES_NUMBERS', ) ?? false, requireSpecialCharacters: aws_cognito_password_protection_settings.passwordPolicyCharacters?.includes( - 'REQUIRES_SYMBOLS' + 'REQUIRES_SYMBOLS', ) ?? false, } : undefined; @@ -175,7 +175,7 @@ export const parseAWSExports = ( new Set([ ...(aws_cognito_verification_mechanisms ?? []), ...(aws_cognito_signup_attributes ?? []), - ]) + ]), ); const userAttributes: AuthConfigUserAttributes = mergedUserAttributes.reduce( @@ -184,7 +184,7 @@ export const parseAWSExports = ( // All user attributes generated by the CLI are required [key.toLowerCase()]: { required: true }, }), - {} + {}, ); const loginWithEmailEnabled = @@ -203,8 +203,7 @@ export const parseAWSExports = ( mfa: mfaConfig, passwordFormat: passwordFormatConfig, loginWith: { - username: - loginWithEmailEnabled || loginWithPhoneEnabled ? false : true, + username: !(loginWithEmailEnabled || loginWithPhoneEnabled), email: loginWithEmailEnabled, phone: loginWithPhoneEnabled, }, @@ -261,6 +260,7 @@ export const parseAWSExports = ( REST: (aws_cloud_logic_custom as any[]).reduce( (acc, api: Record) => { const { name, endpoint, region, service } = api; + return { ...acc, [name]: { @@ -270,7 +270,7 @@ export const parseAWSExports = ( }, }; }, - {} + {}, ), }; } @@ -317,6 +317,7 @@ const getOAuthConfig = ({ const parseSocialProviders = (aws_cognito_social_providers: string[]) => { return aws_cognito_social_providers.map((provider: string) => { const updatedProvider = provider.toLowerCase(); + return updatedProvider.charAt(0).toUpperCase() + updatedProvider.slice(1); }) as OAuthProvider[]; }; diff --git a/packages/core/src/providers/kinesis-firehose/types/kinesis-firehose.ts b/packages/core/src/providers/kinesis-firehose/types/kinesis-firehose.ts index f6b70a46564..c38fb887667 100644 --- a/packages/core/src/providers/kinesis-firehose/types/kinesis-firehose.ts +++ b/packages/core/src/providers/kinesis-firehose/types/kinesis-firehose.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type KinesisFirehoseProviderConfig = { +export interface KinesisFirehoseProviderConfig { KinesisFirehose: { region: string; bufferSize?: number; @@ -9,4 +9,4 @@ export type KinesisFirehoseProviderConfig = { flushInterval?: number; resendLimit?: number; }; -}; +} diff --git a/packages/core/src/providers/kinesis/types/kinesis.ts b/packages/core/src/providers/kinesis/types/kinesis.ts index e4b1ff38d05..7e8954075b3 100644 --- a/packages/core/src/providers/kinesis/types/kinesis.ts +++ b/packages/core/src/providers/kinesis/types/kinesis.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type KinesisProviderConfig = { +export interface KinesisProviderConfig { Kinesis: { region: string; bufferSize?: number; @@ -9,4 +9,4 @@ export type KinesisProviderConfig = { flushInterval?: number; resendLimit?: number; }; -}; +} diff --git a/packages/core/src/providers/personalize/types/personalize.ts b/packages/core/src/providers/personalize/types/personalize.ts index 8eebe4e2068..f34080a1b6c 100644 --- a/packages/core/src/providers/personalize/types/personalize.ts +++ b/packages/core/src/providers/personalize/types/personalize.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type PersonalizeProviderConfig = { +export interface PersonalizeProviderConfig { Personalize: { trackingId: string; region: string; flushSize?: number; flushInterval?: number; }; -}; +} diff --git a/packages/core/src/providers/pinpoint/apis/flushEvents.ts b/packages/core/src/providers/pinpoint/apis/flushEvents.ts index 95c8281771a..7c1f3bbfc91 100644 --- a/packages/core/src/providers/pinpoint/apis/flushEvents.ts +++ b/packages/core/src/providers/pinpoint/apis/flushEvents.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getEventBuffer } from '../utils/getEventBuffer'; -import { EventBufferConfig } from '../types/buffer'; -import { AuthSession } from '../../../singleton/Auth/types'; +import { getEventBuffer } from '~/src/providers/pinpoint/utils/getEventBuffer'; +import { EventBufferConfig } from '~/src/providers/pinpoint/types/buffer'; +import { AuthSession } from '~/src/singleton/Auth/types'; import { BUFFER_SIZE, FLUSH_INTERVAL, FLUSH_SIZE, RESEND_LIMIT, -} from '../utils/constants'; +} from '~/src/providers/pinpoint/utils/constants'; export type PinpointFlushEventsInput = Partial & { appId: string; diff --git a/packages/core/src/providers/pinpoint/apis/record.ts b/packages/core/src/providers/pinpoint/apis/record.ts index 10784514254..a0dbb9deb21 100644 --- a/packages/core/src/providers/pinpoint/apis/record.ts +++ b/packages/core/src/providers/pinpoint/apis/record.ts @@ -1,20 +1,23 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { amplifyUuid } from '../../../utils/amplifyUuid'; -import { PinpointRecordInput, PinpointSession } from '../types'; -import { resolveEndpointId } from '../utils'; +import { amplifyUuid } from '~/src/utils/amplifyUuid'; +import { + PinpointRecordInput, + PinpointSession, +} from '~/src/providers/pinpoint/types'; +import { resolveEndpointId } from '~/src/providers/pinpoint/utils'; import { SESSION_START_EVENT, SESSION_STOP_EVENT, -} from '../../../utils/sessionListener'; +} from '~/src/utils/sessionListener'; import { BUFFER_SIZE, FLUSH_INTERVAL, FLUSH_SIZE, RESEND_LIMIT, -} from '../utils/constants'; -import { getEventBuffer } from '../utils/getEventBuffer'; +} from '~/src/providers/pinpoint/utils/constants'; +import { getEventBuffer } from '~/src/providers/pinpoint/utils/getEventBuffer'; let session: PinpointSession | undefined; diff --git a/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts b/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts index e74c5537c2a..b913187b4c8 100644 --- a/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts +++ b/packages/core/src/providers/pinpoint/apis/updateEndpoint.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { amplifyUuid } from '../../../utils/amplifyUuid'; -import { getClientInfo } from '../../../utils/getClientInfo'; +import { amplifyUuid } from '~/src/utils/amplifyUuid'; +import { getClientInfo } from '~/src/utils/getClientInfo'; import { - updateEndpoint as clientUpdateEndpoint, UpdateEndpointInput, -} from '../../../awsClients/pinpoint'; -import { PinpointUpdateEndpointInput } from '../types'; -import { cacheEndpointId } from '../utils/cacheEndpointId'; -import { getEndpointId } from '../utils/getEndpointId'; + updateEndpoint as clientUpdateEndpoint, +} from '~/src/awsClients/pinpoint'; +import { PinpointUpdateEndpointInput } from '~/src/providers/pinpoint/types'; +import { cacheEndpointId } from '~/src/providers/pinpoint/utils/cacheEndpointId'; +import { getEndpointId } from '~/src/providers/pinpoint/utils/getEndpointId'; /** * @internal @@ -93,7 +93,7 @@ export const updateEndpoint = async ({ }; await clientUpdateEndpoint({ credentials, region, userAgentValue }, input); // if we had to create an endpoint id, we need to now cache it - if (!!createdEndpointId) { + if (createdEndpointId) { return cacheEndpointId(appId, category, createdEndpointId); } }; diff --git a/packages/core/src/providers/pinpoint/types/buffer.ts b/packages/core/src/providers/pinpoint/types/buffer.ts index 28fd0582c44..80a6885fe3c 100644 --- a/packages/core/src/providers/pinpoint/types/buffer.ts +++ b/packages/core/src/providers/pinpoint/types/buffer.ts @@ -1,15 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthSession } from '../../../singleton/Auth/types'; +import { AuthSession } from '~/src/singleton/Auth/types'; + import { PinpointAnalyticsEvent, PinpointSession } from './pinpoint'; -export type EventBufferConfig = { +export interface EventBufferConfig { bufferSize: number; flushInterval: number; flushSize: number; resendLimit: number; -}; +} export type PinpointEventBufferConfig = EventBufferConfig & { appId: string; @@ -19,17 +20,15 @@ export type PinpointEventBufferConfig = EventBufferConfig & { userAgentValue?: string; }; -export type BufferedEvent = { +export interface BufferedEvent { endpointId: string; eventId: string; event: PinpointAnalyticsEvent; session: PinpointSession; timestamp: string; resendLimit: number; -}; +} -export type BufferedEventMap = { - [key: string]: BufferedEvent; -}; +export type BufferedEventMap = Record; -export type EventBuffer = Array; +export type EventBuffer = BufferedEventMap[]; diff --git a/packages/core/src/providers/pinpoint/types/pinpoint.ts b/packages/core/src/providers/pinpoint/types/pinpoint.ts index 91d97aaf273..414baeea877 100644 --- a/packages/core/src/providers/pinpoint/types/pinpoint.ts +++ b/packages/core/src/providers/pinpoint/types/pinpoint.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthSession } from '../../../singleton/Auth/types'; -import { UserProfile } from '../../../types'; +import { AuthSession } from '~/src/singleton/Auth/types'; +import { UserProfile } from '~/src/types'; + import { EventBufferConfig } from './buffer'; export type SupportedCategory = @@ -13,34 +14,34 @@ export type SupportedCategory = type SupportedChannelType = 'APNS' | 'APNS_SANDBOX' | 'GCM' | 'IN_APP'; -export type PinpointProviderConfig = { +export interface PinpointProviderConfig { Pinpoint: Partial & { appId: string; region: string; }; -}; +} -export type PinpointServiceOptions = { +export interface PinpointServiceOptions { address?: string; optOut?: 'ALL' | 'NONE'; userAttributes?: Record; -}; +} -export type PinpointSession = { +export interface PinpointSession { Id: string; Duration?: number; StartTimestamp: string; StopTimestamp?: string; -}; +} -export type PinpointAnalyticsEvent = { +export interface PinpointAnalyticsEvent { name: string; attributes?: Record; metrics?: Record; -}; +} // Common type that is required for operations that may trigger an endpoint update -type PinpointCommonParameters = { +interface PinpointCommonParameters { appId: string; category: SupportedCategory; channelType?: SupportedChannelType; @@ -48,7 +49,7 @@ type PinpointCommonParameters = { identityId?: AuthSession['identityId']; region: string; userAgentValue?: string; -}; +} export type PinpointUpdateEndpointInput = PinpointCommonParameters & PinpointServiceOptions & { diff --git a/packages/core/src/providers/pinpoint/utils/PinpointEventBuffer.ts b/packages/core/src/providers/pinpoint/utils/PinpointEventBuffer.ts index f833e01feca..a9141bad405 100644 --- a/packages/core/src/providers/pinpoint/utils/PinpointEventBuffer.ts +++ b/packages/core/src/providers/pinpoint/utils/PinpointEventBuffer.ts @@ -1,20 +1,21 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../../../Logger'; +import { ConsoleLogger } from '~/src/Logger'; import { EventsBatch, - putEvents, PutEventsInput, PutEventsOutput, -} from '../../../awsClients/pinpoint'; + putEvents, +} from '~/src/awsClients/pinpoint'; import { - PinpointEventBufferConfig, BufferedEvent, BufferedEventMap, EventBuffer, -} from '../types/buffer'; -import { AuthSession } from '../../../singleton/Auth/types'; + PinpointEventBufferConfig, +} from '~/src/providers/pinpoint/types/buffer'; +import { AuthSession } from '~/src/singleton/Auth/types'; + import { isAppInForeground } from './isAppInForeground'; const logger = new ConsoleLogger('PinpointEventBuffer'); @@ -110,16 +111,16 @@ export class PinpointEventBuffer { region, userAgentValue, }, - batchEventParams + batchEventParams, ); this._processPutEventsSuccessResponse(data, eventMap); } catch (err) { - return this._handlePutEventsFailure(err, eventMap); + this._handlePutEventsFailure(err, eventMap); } } private _generateBatchEventParams( - eventMap: BufferedEventMap + eventMap: BufferedEventMap, ): PutEventsInput { const batchItem: Record = {}; @@ -159,18 +160,17 @@ export class PinpointEventBuffer { if (RETRYABLE_CODES.includes(statusCode)) { const retryableEvents = Object.values(eventMap); this._retry(retryableEvents); - return; } } private _processPutEventsSuccessResponse( data: PutEventsOutput, - eventMap: BufferedEventMap + eventMap: BufferedEventMap, ) { const { Results = {} } = data.EventsResponse ?? {}; const retryableEvents: BufferedEvent[] = []; - Object.entries(Results).forEach(([endpointId, endpointValues]) => { + Object.entries(Results).forEach(([_, endpointValues]) => { const responses = endpointValues.EventsItemResponse ?? {}; Object.entries(responses).forEach(([eventId, eventValues]) => { @@ -181,25 +181,13 @@ export class PinpointEventBuffer { const { StatusCode, Message } = eventValues ?? {}; - // manually crafting handlers response to keep API consistant - const response = { - EventsResponse: { - Results: { - [endpointId]: { - EventsItemResponse: { - [eventId]: { StatusCode, Message }, - }, - }, - }, - }, - }; - if (StatusCode && ACCEPTED_CODES.includes(StatusCode)) { return; } if (StatusCode && RETRYABLE_CODES.includes(StatusCode)) { retryableEvents.push(eventObject); + return; } @@ -233,6 +221,7 @@ export class PinpointEventBuffer { remainingAttempts: bufferedEvent.resendLimit, }); eligibleEvents.push({ [eventId!]: bufferedEvent }); + return; } @@ -250,6 +239,7 @@ export class PinpointEventBuffer { return buffer.reduce((acc, curVal) => { const [[key, value]] = Object.entries(curVal); acc[key] = value; + return acc; }, {}); } diff --git a/packages/core/src/providers/pinpoint/utils/cacheEndpointId.ts b/packages/core/src/providers/pinpoint/utils/cacheEndpointId.ts index d34ac4c796a..51571fde377 100644 --- a/packages/core/src/providers/pinpoint/utils/cacheEndpointId.ts +++ b/packages/core/src/providers/pinpoint/utils/cacheEndpointId.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Cache } from '../../../Cache'; -import { SupportedCategory } from '../types'; +import { Cache } from '~/src/Cache'; +import { SupportedCategory } from '~/src/providers/pinpoint/types'; + import { getCacheKey } from './getCacheKey'; /** @@ -13,13 +14,14 @@ import { getCacheKey } from './getCacheKey'; export const cacheEndpointId = async ( appId: string, category: SupportedCategory, - endpointId: string + endpointId: string, ): Promise => { const cacheKey = getCacheKey(appId, category); // Set a longer TTL to avoid endpoint id being deleted after the default TTL (3 days) // Also set its priority to the highest to reduce its chance of being deleted when cache is full const ttl = 1000 * 60 * 60 * 24 * 365 * 100; // 100 years const expiration = new Date().getTime() + ttl; + return Cache.setItem(cacheKey, endpointId, { expires: expiration, priority: 1, diff --git a/packages/core/src/providers/pinpoint/utils/getCacheKey.ts b/packages/core/src/providers/pinpoint/utils/getCacheKey.ts index 346bb1debec..fb50c92fe0a 100644 --- a/packages/core/src/providers/pinpoint/utils/getCacheKey.ts +++ b/packages/core/src/providers/pinpoint/utils/getCacheKey.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SupportedCategory } from '../types'; +import { SupportedCategory } from '~/src/providers/pinpoint/types'; const PROVIDER_NAME = 'pinpoint'; @@ -12,5 +12,5 @@ const PROVIDER_NAME = 'pinpoint'; */ export const getCacheKey = ( appId: string, - category: SupportedCategory + category: SupportedCategory, ): string => `${category}:${PROVIDER_NAME}:${appId}`; diff --git a/packages/core/src/providers/pinpoint/utils/getEndpointId.ts b/packages/core/src/providers/pinpoint/utils/getEndpointId.ts index a8b6492d4bd..9bf0f66f7ba 100644 --- a/packages/core/src/providers/pinpoint/utils/getEndpointId.ts +++ b/packages/core/src/providers/pinpoint/utils/getEndpointId.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Cache } from '../../../Cache'; -import { SupportedCategory } from '../types'; +import { Cache } from '~/src/Cache'; +import { SupportedCategory } from '~/src/providers/pinpoint/types'; + import { getCacheKey } from './getCacheKey'; /** @@ -12,9 +13,10 @@ import { getCacheKey } from './getCacheKey'; */ export const getEndpointId = async ( appId: string, - category: SupportedCategory + category: SupportedCategory, ): Promise => { const cacheKey = getCacheKey(appId, category); const cachedEndpointId = await Cache.getItem(cacheKey); + return cachedEndpointId ?? undefined; }; diff --git a/packages/core/src/providers/pinpoint/utils/getEventBuffer.ts b/packages/core/src/providers/pinpoint/utils/getEventBuffer.ts index 0f737391915..220078825dd 100644 --- a/packages/core/src/providers/pinpoint/utils/getEventBuffer.ts +++ b/packages/core/src/providers/pinpoint/utils/getEventBuffer.ts @@ -1,9 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventBufferConfig } from '../types/buffer'; +import { EventBufferConfig } from '~/src/providers/pinpoint/types/buffer'; +import { AuthSession } from '~/src/singleton/Auth/types'; + import { PinpointEventBuffer } from './PinpointEventBuffer'; -import { AuthSession } from '../../../singleton/Auth/types'; // Map of buffers by region -> appId const eventBufferMap: Record> = {}; diff --git a/packages/core/src/providers/pinpoint/utils/isAppInForeground.native.ts b/packages/core/src/providers/pinpoint/utils/isAppInForeground.native.ts index cb09e48f15c..906eeacbf02 100644 --- a/packages/core/src/providers/pinpoint/utils/isAppInForeground.native.ts +++ b/packages/core/src/providers/pinpoint/utils/isAppInForeground.native.ts @@ -1,6 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +// TODO(eslint): remove this linter suppression. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: missing type definition import { AppState } from 'react-native'; diff --git a/packages/core/src/providers/pinpoint/utils/resolveEndpointId.ts b/packages/core/src/providers/pinpoint/utils/resolveEndpointId.ts index 6cf3ccdc1ce..19ceee3f12d 100644 --- a/packages/core/src/providers/pinpoint/utils/resolveEndpointId.ts +++ b/packages/core/src/providers/pinpoint/utils/resolveEndpointId.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { assert } from '../../../errors'; -import { AmplifyErrorCode } from '../../../types'; -import { updateEndpoint } from '../apis/updateEndpoint'; -import { PinpointUpdateEndpointInput } from '../types'; +import { assert } from '~/src/errors'; +import { AmplifyErrorCode } from '~/src/types'; +import { updateEndpoint } from '~/src/providers/pinpoint/apis/updateEndpoint'; +import { PinpointUpdateEndpointInput } from '~/src/providers/pinpoint/types'; + import { getEndpointId } from './getEndpointId'; /** diff --git a/packages/core/src/singleton/API/types.ts b/packages/core/src/singleton/API/types.ts index ca0d17b0f9c..03999d44148 100644 --- a/packages/core/src/singleton/API/types.ts +++ b/packages/core/src/singleton/API/types.ts @@ -1,24 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Headers } from '../../clients'; -import { AtLeastOne } from '../types'; +import { Headers } from '~/src/clients'; +import { AtLeastOne } from '~/src/singleton/types'; -export type LibraryAPIOptions = { +export interface LibraryAPIOptions { GraphQL?: { // custom headers for given GraphQL service. Will be applied to all operations. - headers?: (options?: { + headers?(options?: { query?: string; variables?: Record; - }) => Promise; + }): Promise>; withCredentials?: boolean; }; REST?: { // custom headers for given REST service. Will be applied to all operations. - headers?: (options: { apiName: string }) => Promise; + headers?(options: { apiName: string }): Promise; }; -}; +} -type APIGraphQLConfig = { +interface APIGraphQLConfig { /** * Required GraphQL endpoint, must be a valid URL string. */ @@ -45,9 +45,9 @@ type APIGraphQLConfig = { */ defaultAuthMode: GraphQLAuthMode; modelIntrospection?: ModelIntrospectionSchema; -}; +} -type APIRestConfig = { +interface APIRestConfig { /** * Required REST endpoint, must be a valid URL string. */ @@ -65,15 +65,15 @@ type APIRestConfig = { * @default 'execute-api' */ service?: string; -}; +} -export type RESTProviderConfig = { +export interface RESTProviderConfig { REST: Record; -}; +} -export type GraphQLProviderConfig = { +export interface GraphQLProviderConfig { GraphQL: APIGraphQLConfig; -}; +} export type APIConfig = AtLeastOne; @@ -101,12 +101,12 @@ export type DocumentType = * * Borrowed from: https://github.com/aws-amplify/samsara-cli/pull/377/commits/c08ea2c1a43f36aafe63b6d14d03f884e9c0c671#diff-21ae6faa2f22c15bb25ff9b272eaab7846c0650e2d267ab720546c19559583d0R4-R108 */ -export type ModelIntrospectionSchema = { +export interface ModelIntrospectionSchema { version: 1; models: SchemaModels; nonModels: SchemaNonModels; enums: SchemaEnums; -}; +} /** * Top-level Entities on a Schema @@ -115,33 +115,33 @@ export type SchemaModels = Record; export type SchemaNonModels = Record; export type SchemaEnums = Record; -export type SchemaModel = { +export interface SchemaModel { name: string; attributes?: ModelAttribute[]; fields: Fields; pluralName: string; syncable?: boolean; primaryKeyInfo: PrimaryKeyInfo; -}; -export type SchemaNonModel = { +} +export interface SchemaNonModel { name: string; fields: Fields; -}; -export type SchemaEnum = { +} +export interface SchemaEnum { name: string; values: string[]; -}; +} -export type ModelAttribute = { +export interface ModelAttribute { type: string; - properties?: { [key: string]: any }; -}; + properties?: Record; +} /** * Field Definition */ export type Fields = Record; -export type Field = { +export interface Field { name: string; type: FieldType; isArray: boolean; @@ -150,9 +150,11 @@ export type Field = { isArrayNullable?: boolean; attributes?: FieldAttribute[]; association?: AssociationType; -}; +} -export type ModelFieldType = { model: string }; +export interface ModelFieldType { + model: string; +} export type FieldType = | 'ID' @@ -182,9 +184,9 @@ export enum CodeGenConnectionType { BELONGS_TO = 'BELONGS_TO', HAS_MANY = 'HAS_MANY', } -export type AssociationBaseType = { +export interface AssociationBaseType { connectionType: CodeGenConnectionType; -}; +} export type AssociationHasMany = AssociationBaseType & { connectionType: CodeGenConnectionType.HAS_MANY; @@ -205,8 +207,8 @@ export type AssociationType = | AssociationHasOne | AssociationBelongsTo; -export type PrimaryKeyInfo = { +export interface PrimaryKeyInfo { isCustomPrimaryKey: boolean; primaryKeyFieldName: string; sortKeyFieldNames: string[]; -}; +} diff --git a/packages/core/src/singleton/Amplify.ts b/packages/core/src/singleton/Amplify.ts index 3040c95c8b6..3ab440886df 100644 --- a/packages/core/src/singleton/Amplify.ts +++ b/packages/core/src/singleton/Amplify.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthClass } from './Auth'; -import { Hub, AMPLIFY_SYMBOL } from '../Hub'; +import { AMPLIFY_SYMBOL, Hub } from '~/src/Hub'; +import { parseAWSExports } from '~/src/parseAWSExports'; +import { deepFreeze } from '~/src/utils'; + import { LegacyConfig, LibraryOptions, ResourcesConfig } from './types'; -import { parseAWSExports } from '../parseAWSExports'; -import { deepFreeze } from '../utils'; +import { AuthClass } from './Auth'; export class AmplifyClass { resourcesConfig: ResourcesConfig; @@ -38,7 +39,7 @@ export class AmplifyClass { */ configure( resourcesConfig: ResourcesConfig | LegacyConfig, - libraryOptions?: LibraryOptions + libraryOptions?: LibraryOptions, ): void { let resolvedResourceConfig: ResourcesConfig; @@ -66,7 +67,7 @@ export class AmplifyClass { data: resourcesConfig, }, 'Configure', - AMPLIFY_SYMBOL + AMPLIFY_SYMBOL, ); } diff --git a/packages/core/src/singleton/Analytics/types.ts b/packages/core/src/singleton/Analytics/types.ts index 59eb2ef1141..a3a6d3729ba 100644 --- a/packages/core/src/singleton/Analytics/types.ts +++ b/packages/core/src/singleton/Analytics/types.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PinpointProviderConfig } from '../../providers/pinpoint/types'; -import { KinesisProviderConfig } from '../../providers/kinesis/types'; -import { KinesisFirehoseProviderConfig } from '../../providers/kinesis-firehose/types'; -import { PersonalizeProviderConfig } from '../../providers/personalize/types'; -import { AtLeastOne } from '../types'; +import { PinpointProviderConfig } from '~/src/providers/pinpoint/types'; +import { KinesisProviderConfig } from '~/src/providers/kinesis/types'; +import { KinesisFirehoseProviderConfig } from '~/src/providers/kinesis-firehose/types'; +import { PersonalizeProviderConfig } from '~/src/providers/personalize/types'; +import { AtLeastOne } from '~/src/singleton/types'; export type AnalyticsConfig = AtLeastOne< PinpointProviderConfig & diff --git a/packages/core/src/singleton/Auth/index.ts b/packages/core/src/singleton/Auth/index.ts index d43f508674c..c4a87e58970 100644 --- a/packages/core/src/singleton/Auth/index.ts +++ b/packages/core/src/singleton/Auth/index.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - CredentialsAndIdentityId, AuthConfig, AuthSession, AuthTokens, + CredentialsAndIdentityId, FetchAuthSessionOptions, LibraryAuthOptions, } from './types'; @@ -17,6 +17,7 @@ export function isTokenExpired({ clockDrift: number; }): boolean { const currentTime = Date.now(); + return currentTime + clockDrift > expiresAt; } @@ -24,8 +25,6 @@ export class AuthClass { private authConfig?: AuthConfig; private authOptions?: LibraryAuthOptions; - constructor() {} - /** * Configure Auth category * @@ -38,21 +37,20 @@ export class AuthClass { */ configure( authResourcesConfig: AuthConfig, - authOptions?: LibraryAuthOptions + authOptions?: LibraryAuthOptions, ): void { this.authConfig = authResourcesConfig; this.authOptions = authOptions; } async fetchAuthSession( - options: FetchAuthSessionOptions = {} + options: FetchAuthSessionOptions = {}, ): Promise { - let tokens: AuthTokens | undefined; let credentialsAndIdentityId: CredentialsAndIdentityId | undefined; let userSub: string | undefined; // Get tokens will throw if session cannot be refreshed (network or service error) or return null if not available - tokens = await this.getTokens(options); + const tokens = await this.getTokens(options); if (tokens) { userSub = tokens.accessToken?.payload?.sub; @@ -65,7 +63,7 @@ export class AuthClass { tokens, authenticated: true, forceRefresh: options.forceRefresh, - } + }, ); } else { // getCredentialsAndIdentityId will throw if cannot get credentials (network or service error) @@ -75,7 +73,7 @@ export class AuthClass { authConfig: this.authConfig, authenticated: false, forceRefresh: options.forceRefresh, - } + }, ); } @@ -89,12 +87,12 @@ export class AuthClass { async clearCredentials(): Promise { if (this.authOptions?.credentialsProvider) { - return await this.authOptions.credentialsProvider.clearCredentialsAndIdentityId(); + await this.authOptions.credentialsProvider.clearCredentialsAndIdentityId(); } } async getTokens( - options: FetchAuthSessionOptions + options: FetchAuthSessionOptions, ): Promise { return ( (await this.authOptions?.tokenProvider?.getTokens(options)) ?? undefined diff --git a/packages/core/src/singleton/Auth/types.ts b/packages/core/src/singleton/Auth/types.ts index 44fac6f0a78..7094b75fc86 100644 --- a/packages/core/src/singleton/Auth/types.ts +++ b/packages/core/src/singleton/Auth/types.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AtLeastOne } from '../types'; +import { AtLeastOne } from '~/src/singleton/types'; // From https://github.com/awslabs/aws-jwt-verify/blob/main/src/safe-json-parse.ts // From https://github.com/awslabs/aws-jwt-verify/blob/main/src/jwt-model.ts @@ -16,61 +16,65 @@ interface JwtPayloadStandardFields { sub?: string; // JWT sub https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2 } -/** JSON type */ -type Json = null | string | number | boolean | Json[] | JsonObject; +type JsonPrimitive = null | string | number | boolean; + +/** JSON array type */ +type JsonArray = JsonPrimitive[]; /** JSON Object type */ -type JsonObject = { [name: string]: Json }; +interface JsonObject { + [x: string]: JsonPrimitive | JsonArray | JsonObject; +} export type JwtPayload = JwtPayloadStandardFields & JsonObject; -export type JWT = { +export interface JWT { payload: JwtPayload; - toString: () => string; -}; + toString(): string; +} export type JWTCreator = (stringJWT: string) => JWT; -export type AuthSession = { +export interface AuthSession { tokens?: AuthTokens; credentials?: AWSCredentials; identityId?: string; userSub?: string; -}; +} -export type LibraryAuthOptions = { +export interface LibraryAuthOptions { tokenProvider?: TokenProvider; credentialsProvider?: CredentialsAndIdentityIdProvider; -}; +} -export type Identity = { +export interface Identity { id: string; type: 'guest' | 'primary'; -}; +} export interface CredentialsAndIdentityIdProvider { - getCredentialsAndIdentityId: ( - getCredentialsOptions: GetCredentialsOptions - ) => Promise; - clearCredentialsAndIdentityId: () => void; + getCredentialsAndIdentityId( + getCredentialsOptions: GetCredentialsOptions, + ): Promise; + clearCredentialsAndIdentityId(): Promise; } -export type TokenProvider = { - getTokens: ({ +export interface TokenProvider { + getTokens({ forceRefresh, }?: { forceRefresh?: boolean; - }) => Promise; -}; + }): Promise; +} -export type FetchAuthSessionOptions = { +export interface FetchAuthSessionOptions { forceRefresh?: boolean; -}; +} -export type AuthTokens = { +export interface AuthTokens { idToken?: JWT; accessToken: JWT; -}; +} export type AuthStandardAttributeKey = | 'address' @@ -116,7 +120,7 @@ type StrictUnionHelper = T extends any : never; export type StrictUnion = StrictUnionHelper; -export type AuthIdentityPoolConfig = { +export interface AuthIdentityPoolConfig { Cognito: CognitoIdentityPoolConfig & { userPoolClientId?: never; userPoolId?: never; @@ -127,21 +131,21 @@ export type AuthIdentityPoolConfig = { mfa?: never; passwordFormat?: never; }; -}; +} -export type CognitoIdentityPoolConfig = { +export interface CognitoIdentityPoolConfig { identityPoolId: string; allowGuestAccess?: boolean; -}; +} -export type AuthUserPoolConfig = { +export interface AuthUserPoolConfig { Cognito: CognitoUserPoolConfig & { identityPoolId?: never; allowGuestAccess?: never; }; -}; +} -export type CognitoUserPoolConfig = { +export interface CognitoUserPoolConfig { userPoolClientId: string; userPoolId: string; userPoolEndpoint?: string; @@ -165,21 +169,23 @@ export type CognitoUserPoolConfig = { requireNumbers?: boolean; requireSpecialCharacters?: boolean; }; -}; +} -export type OAuthConfig = { +export interface OAuthConfig { domain: string; - scopes: Array; - redirectSignIn: Array; - redirectSignOut: Array; + scopes: OAuthScope[]; + redirectSignIn: string[]; + redirectSignOut: string[]; responseType: 'code' | 'token'; - providers?: Array; -}; + providers?: (OAuthProvider | CustomProvider)[]; +} export type OAuthProvider = 'Google' | 'Facebook' | 'Amazon' | 'Apple'; -type CustomProvider = { custom: string }; +interface CustomProvider { + custom: string; +} -type CustomScope = string & {}; +type CustomScope = string; type OAuthScope = | 'email' | 'openid' @@ -194,9 +200,9 @@ export type CognitoUserPoolWithOAuthConfig = CognitoUserPoolConfig & { oauth: OAuthConfig; }; }; -export type AuthUserPoolAndIdentityPoolConfig = { +export interface AuthUserPoolAndIdentityPoolConfig { Cognito: CognitoUserPoolAndIdentityPoolConfig; -}; +} export type CognitoUserPoolAndIdentityPoolConfig = CognitoUserPoolConfig & CognitoIdentityPoolConfig; @@ -205,28 +211,28 @@ export type GetCredentialsOptions = | GetCredentialsAuthenticatedUser | GetCredentialsUnauthenticatedUser; -type GetCredentialsAuthenticatedUser = { +interface GetCredentialsAuthenticatedUser { authenticated: true; forceRefresh?: boolean; authConfig: AuthConfig | undefined; tokens: AuthTokens; -}; +} -type GetCredentialsUnauthenticatedUser = { +interface GetCredentialsUnauthenticatedUser { authenticated: false; forceRefresh?: boolean; authConfig: AuthConfig | undefined; tokens?: never; -}; +} -export type CredentialsAndIdentityId = { +export interface CredentialsAndIdentityId { credentials: AWSCredentials; identityId?: string; -}; +} -export type AWSCredentials = { +export interface AWSCredentials { accessKeyId: string; secretAccessKey: string; sessionToken?: string; expiration?: Date; -}; +} diff --git a/packages/core/src/singleton/Auth/utils/errorHelpers.ts b/packages/core/src/singleton/Auth/utils/errorHelpers.ts index a8cf1c9b3b9..7f5e559e51d 100644 --- a/packages/core/src/singleton/Auth/utils/errorHelpers.ts +++ b/packages/core/src/singleton/Auth/utils/errorHelpers.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { createAssertionFunction } from '../../../errors'; -import { AmplifyErrorMap, AssertionFunction } from '../../../types'; +import { createAssertionFunction } from '~/src/errors'; +import { AmplifyErrorMap, AssertionFunction } from '~/src/types'; export enum AuthConfigurationErrorCode { AuthTokenConfigException = 'AuthTokenConfigException', diff --git a/packages/core/src/singleton/Auth/utils/index.ts b/packages/core/src/singleton/Auth/utils/index.ts index 43301206b44..25cc2a45534 100644 --- a/packages/core/src/singleton/Auth/utils/index.ts +++ b/packages/core/src/singleton/Auth/utils/index.ts @@ -1,25 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthConfigurationErrorCode, assert } from './errorHelpers'; -import { base64Decoder } from '../../../utils/convert'; +import { base64Decoder } from '~/src/utils/convert'; import { AuthConfig, - JWT, - AuthUserPoolAndIdentityPoolConfig, - CognitoUserPoolWithOAuthConfig, - CognitoUserPoolConfig, - CognitoUserPoolAndIdentityPoolConfig, CognitoIdentityPoolConfig, - StrictUnion, + CognitoUserPoolAndIdentityPoolConfig, + CognitoUserPoolConfig, + JWT, OAuthConfig, -} from '../types'; + StrictUnion, +} from '~/src/singleton/Auth/types'; + +import { AuthConfigurationErrorCode, assert } from './errorHelpers'; export function assertTokenProviderConfig( cognitoConfig?: StrictUnion< | CognitoUserPoolConfig | CognitoUserPoolAndIdentityPoolConfig | CognitoIdentityPoolConfig - > + >, ): asserts cognitoConfig is | CognitoUserPoolAndIdentityPoolConfig | CognitoUserPoolConfig { @@ -31,14 +30,11 @@ export function assertTokenProviderConfig( !!cognitoConfig.userPoolId && !!cognitoConfig.userPoolClientId; } - return assert( - assertionValid, - AuthConfigurationErrorCode.AuthUserPoolException - ); + assert(assertionValid, AuthConfigurationErrorCode.AuthUserPoolException); } export function assertOAuthConfig( - cognitoConfig?: AuthConfig['Cognito'] + cognitoConfig?: AuthConfig['Cognito'], ): asserts cognitoConfig is AuthConfig['Cognito'] & { loginWith: { oauth: OAuthConfig; @@ -49,9 +45,10 @@ export function assertOAuthConfig( !!cognitoConfig?.loginWith?.oauth?.redirectSignOut && !!cognitoConfig?.loginWith?.oauth?.redirectSignIn && !!cognitoConfig?.loginWith?.oauth?.responseType; - return assert( + + assert( validOAuthConfig, - AuthConfigurationErrorCode.OAuthNotConfigureException + AuthConfigurationErrorCode.OAuthNotConfigureException, ); } export function assertIdentityPoolIdConfig( @@ -59,23 +56,13 @@ export function assertIdentityPoolIdConfig( | CognitoUserPoolConfig | CognitoUserPoolAndIdentityPoolConfig | CognitoIdentityPoolConfig - > + >, ): asserts cognitoConfig is CognitoIdentityPoolConfig { const validConfig = !!cognitoConfig?.identityPoolId; - return assert( - validConfig, - AuthConfigurationErrorCode.InvalidIdentityPoolIdException - ); -} -function assertUserPoolAndIdentityPoolConfig( - authConfig: AuthConfig -): asserts authConfig is AuthUserPoolAndIdentityPoolConfig { - const validConfig = - !!authConfig?.Cognito.identityPoolId && !!authConfig?.Cognito.userPoolId; - return assert( + assert( validConfig, - AuthConfigurationErrorCode.AuthUserPoolAndIdentityPoolException + AuthConfigurationErrorCode.InvalidIdentityPoolIdException, ); } @@ -94,7 +81,7 @@ export function decodeJWT(token: string): JWT { .convert(base64) .split('') .map(char => `%${`00${char.charCodeAt(0).toString(16)}`.slice(-2)}`) - .join('') + .join(''), ); const payload = JSON.parse(jsonStr); diff --git a/packages/core/src/singleton/Geo/types.ts b/packages/core/src/singleton/Geo/types.ts index ac275df6e22..42e19e685a9 100644 --- a/packages/core/src/singleton/Geo/types.ts +++ b/packages/core/src/singleton/Geo/types.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AtLeastOne } from '../types'; +import { AtLeastOne } from '~/src/singleton/types'; -export type LocationServiceConfig = { +export interface LocationServiceConfig { LocationService: { region: string; maps?: { - items: {}; + items: object; default: string; }; searchIndices?: { @@ -19,6 +19,6 @@ export type LocationServiceConfig = { default: string; }; }; -}; +} export type GeoConfig = AtLeastOne; diff --git a/packages/core/src/singleton/Interactions/types.ts b/packages/core/src/singleton/Interactions/types.ts index e62fe13d1c8..9c31b7ccd97 100644 --- a/packages/core/src/singleton/Interactions/types.ts +++ b/packages/core/src/singleton/Interactions/types.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AtLeastOne } from '../types'; +import { AtLeastOne } from '~/src/singleton/types'; interface LexV1BotConfig { alias: string; @@ -15,13 +15,13 @@ interface LexV2BotConfig { region: string; } -type InteractionsLexV1Config = { +interface InteractionsLexV1Config { LexV1: Record; -}; +} -type InteractionsLexV2Config = { +interface InteractionsLexV2Config { LexV2: Record; -}; +} export type InteractionsConfig = AtLeastOne< InteractionsLexV1Config & InteractionsLexV2Config diff --git a/packages/core/src/singleton/Notifications/InAppMessaging/types.ts b/packages/core/src/singleton/Notifications/InAppMessaging/types.ts index 3a2f6d99033..d7a9a863b1b 100644 --- a/packages/core/src/singleton/Notifications/InAppMessaging/types.ts +++ b/packages/core/src/singleton/Notifications/InAppMessaging/types.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PinpointProviderConfig } from '../../../providers/pinpoint/types'; +import { PinpointProviderConfig } from '~/src/providers/pinpoint/types'; export type InAppMessagingConfig = PinpointProviderConfig; diff --git a/packages/core/src/singleton/Notifications/PushNotification/types.ts b/packages/core/src/singleton/Notifications/PushNotification/types.ts index 3e599118a73..825a1ca24a6 100644 --- a/packages/core/src/singleton/Notifications/PushNotification/types.ts +++ b/packages/core/src/singleton/Notifications/PushNotification/types.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PinpointProviderConfig } from '../../../providers/pinpoint/types'; +import { PinpointProviderConfig } from '~/src/providers/pinpoint/types'; export type PushNotificationConfig = PinpointProviderConfig; diff --git a/packages/core/src/singleton/Notifications/types.ts b/packages/core/src/singleton/Notifications/types.ts index 85ef4ef8481..0247a880f62 100644 --- a/packages/core/src/singleton/Notifications/types.ts +++ b/packages/core/src/singleton/Notifications/types.ts @@ -1,17 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { AtLeastOne } from '~/src/singleton/types'; + import { InAppMessagingConfig } from './InAppMessaging/types'; import { PushNotificationConfig } from './PushNotification/types'; -import { AtLeastOne } from '../types'; -export type InAppMessagingProviderConfig = { +export interface InAppMessagingProviderConfig { InAppMessaging: InAppMessagingConfig; -}; +} -export type PushNotificationProviderConfig = { +export interface PushNotificationProviderConfig { PushNotification: PushNotificationConfig; -}; +} export type NotificationsConfig = AtLeastOne< InAppMessagingProviderConfig & PushNotificationProviderConfig diff --git a/packages/core/src/singleton/Predictions/types.ts b/packages/core/src/singleton/Predictions/types.ts index 022e9379771..1862a9f5309 100644 --- a/packages/core/src/singleton/Predictions/types.ts +++ b/packages/core/src/singleton/Predictions/types.ts @@ -1,70 +1,70 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AtLeastOne } from '../types'; +import { AtLeastOne } from '~/src/singleton/types'; // Defaults for ConvertConfig -type SpeechGeneratorDefaults = { +interface SpeechGeneratorDefaults { voiceId?: string; -}; -type TranscriptionDefaults = { +} +interface TranscriptionDefaults { language?: string; -}; -type TranslateTextDefaults = { +} +interface TranslateTextDefaults { sourceLanguage?: string; targetLanguage?: string; -}; +} // Defaults for IdentifyConfig -type IdentifyEntitiesDefaults = { +interface IdentifyEntitiesDefaults { collectionId?: string; maxEntities?: number; -}; -type IdentityLabelsDefaults = { +} +interface IdentityLabelsDefaults { type?: string; -}; -type IdentifyTextDefaults = { +} +interface IdentifyTextDefaults { format?: string; -}; +} // Defaults for InterpretConfig -type InterpretTextDefaults = { +interface InterpretTextDefaults { type?: string; -}; +} -type ConvertConfig = { +interface ConvertConfig { speechGenerator?: PredictionsProviderConfig; transcription?: PredictionsProviderConfig; translateText?: PredictionsProviderConfig; -}; +} -type IdentifyConfig = { +interface IdentifyConfig { identifyEntities?: PredictionsProviderConfig & { celebrityDetectionEnabled?: boolean; }; identifyLabels?: PredictionsProviderConfig; identifyText?: PredictionsProviderConfig; -}; +} -type InterpretConfig = { +interface InterpretConfig { interpretText?: PredictionsProviderConfig; -}; +} -export type PredictionsProviderConfig = { +export interface PredictionsProviderConfig { region?: string; proxy?: boolean; defaults?: T; -}; +} -export type PredictionsConvertConfig = { +export interface PredictionsConvertConfig { convert: ConvertConfig; -}; -export type PredictionsIdentifyConfig = { +} +export interface PredictionsIdentifyConfig { identify: IdentifyConfig; -}; -export type PredictionsInterpretConfig = { +} +export interface PredictionsInterpretConfig { interpret: InterpretConfig; -}; +} export type PredictionsConfig = AtLeastOne< PredictionsConvertConfig & diff --git a/packages/core/src/singleton/Storage/types.ts b/packages/core/src/singleton/Storage/types.ts index 6864064287a..cf7a62a5ae5 100644 --- a/packages/core/src/singleton/Storage/types.ts +++ b/packages/core/src/singleton/Storage/types.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AtLeastOne } from '../types'; +import { AtLeastOne } from '~/src/singleton/types'; export type StorageAccessLevel = 'guest' | 'protected' | 'private'; -export type S3ProviderConfig = { +export interface S3ProviderConfig { S3: { bucket?: string; region?: string; @@ -16,7 +16,7 @@ export type S3ProviderConfig = { */ dangerouslyConnectToHttpEndpointForTesting?: string; }; -}; +} export type StorageConfig = AtLeastOne; diff --git a/packages/core/src/singleton/apis/clearCredentials.ts b/packages/core/src/singleton/apis/clearCredentials.ts index 18d658a7265..4971dcfe658 100644 --- a/packages/core/src/singleton/apis/clearCredentials.ts +++ b/packages/core/src/singleton/apis/clearCredentials.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Amplify } from '../Amplify'; +import { Amplify } from '~/src/singleton/Amplify'; export function clearCredentials(): Promise { return Amplify.Auth.clearCredentials(); diff --git a/packages/core/src/singleton/apis/fetchAuthSession.ts b/packages/core/src/singleton/apis/fetchAuthSession.ts index 63629cabe92..c2d2c649db0 100644 --- a/packages/core/src/singleton/apis/fetchAuthSession.ts +++ b/packages/core/src/singleton/apis/fetchAuthSession.ts @@ -1,12 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { Amplify } from '~/src/singleton/Amplify'; +import { + AuthSession, + FetchAuthSessionOptions, +} from '~/src/singleton/Auth/types'; + import { fetchAuthSession as fetchAuthSessionInternal } from './internal/fetchAuthSession'; -import { Amplify } from '../Amplify'; -import { AuthSession, FetchAuthSessionOptions } from '../Auth/types'; export const fetchAuthSession = ( - options?: FetchAuthSessionOptions + options?: FetchAuthSessionOptions, ): Promise => { return fetchAuthSessionInternal(Amplify, options); }; diff --git a/packages/core/src/singleton/apis/internal/fetchAuthSession.ts b/packages/core/src/singleton/apis/internal/fetchAuthSession.ts index 472276663a9..134eb060bef 100644 --- a/packages/core/src/singleton/apis/internal/fetchAuthSession.ts +++ b/packages/core/src/singleton/apis/internal/fetchAuthSession.ts @@ -1,12 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyClass } from '../../Amplify'; -import { AuthSession, FetchAuthSessionOptions } from '../../Auth/types'; +import { AmplifyClass } from '~/src/singleton/Amplify'; +import { + AuthSession, + FetchAuthSessionOptions, +} from '~/src/singleton/Auth/types'; export const fetchAuthSession = ( amplify: AmplifyClass, - options?: FetchAuthSessionOptions + options?: FetchAuthSessionOptions, ): Promise => { return amplify.Auth.fetchAuthSession(options); }; diff --git a/packages/core/src/singleton/apis/server/fetchAuthSession.ts b/packages/core/src/singleton/apis/server/fetchAuthSession.ts index 04cca6b2556..3b007ccb8cb 100644 --- a/packages/core/src/singleton/apis/server/fetchAuthSession.ts +++ b/packages/core/src/singleton/apis/server/fetchAuthSession.ts @@ -1,16 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyServer, getAmplifyServerContext } from '../../../adapterCore'; -import { AuthSession, FetchAuthSessionOptions } from '../../Auth/types'; -import { fetchAuthSession as fetchAuthSessionInternal } from '../internal/fetchAuthSession'; +import { AmplifyServer, getAmplifyServerContext } from '~/src/adapterCore'; +import { + AuthSession, + FetchAuthSessionOptions, +} from '~/src/singleton/Auth/types'; +import { fetchAuthSession as fetchAuthSessionInternal } from '~/src/singleton/apis/internal/fetchAuthSession'; export const fetchAuthSession = ( contextSpec: AmplifyServer.ContextSpec, - options?: FetchAuthSessionOptions + options?: FetchAuthSessionOptions, ): Promise => { return fetchAuthSessionInternal( getAmplifyServerContext(contextSpec).amplify, - options + options, ); }; diff --git a/packages/core/src/singleton/types.ts b/packages/core/src/singleton/types.ts index a3787dc4b75..174db17fc5c 100644 --- a/packages/core/src/singleton/types.ts +++ b/packages/core/src/singleton/types.ts @@ -5,12 +5,12 @@ import { APIConfig, LibraryAPIOptions } from './API/types'; import { AnalyticsConfig } from './Analytics/types'; import { AuthConfig, - LibraryAuthOptions, - AuthUserPoolConfig, AuthIdentityPoolConfig, AuthUserPoolAndIdentityPoolConfig, - GetCredentialsOptions, + AuthUserPoolConfig, CognitoIdentityPoolConfig, + GetCredentialsOptions, + LibraryAuthOptions, } from './Auth/types'; import { GeoConfig } from './Geo/types'; import { PredictionsConfig } from './Predictions/types'; @@ -22,17 +22,17 @@ import { import { NotificationsConfig } from './Notifications/types'; import { InteractionsConfig } from './Interactions/types'; -export type LegacyConfig = { +export interface LegacyConfig { /** * @deprecated The field should not be used. */ aws_project_region?: string; -}; +} export type AtLeastOne }> = Partial & U[keyof U]; -export type ResourcesConfig = { +export interface ResourcesConfig { API?: APIConfig; Analytics?: AnalyticsConfig; Auth?: AuthConfig; @@ -41,14 +41,14 @@ export type ResourcesConfig = { Predictions?: PredictionsConfig; Storage?: StorageConfig; Geo?: GeoConfig; -}; +} -export type LibraryOptions = { +export interface LibraryOptions { API?: LibraryAPIOptions; Auth?: LibraryAuthOptions; Storage?: LibraryStorageOptions; ssr?: boolean; -}; +} export { APIConfig, diff --git a/packages/core/src/storage/CookieStorage.native.ts b/packages/core/src/storage/CookieStorage.native.ts index c5e3513a6d3..b396e713742 100644 --- a/packages/core/src/storage/CookieStorage.native.ts +++ b/packages/core/src/storage/CookieStorage.native.ts @@ -6,8 +6,4 @@ import { KeyValueStorage } from './KeyValueStorage'; /** * @internal */ -export class CookieStorage extends KeyValueStorage { - constructor() { - super(); - } -} +export class CookieStorage extends KeyValueStorage {} diff --git a/packages/core/src/storage/CookieStorage.ts b/packages/core/src/storage/CookieStorage.ts index a20ab95b3dd..d308735b910 100644 --- a/packages/core/src/storage/CookieStorage.ts +++ b/packages/core/src/storage/CookieStorage.ts @@ -6,7 +6,7 @@ import { CookieStorageData, KeyValueStorageInterface, SameSite, -} from '../types'; +} from '~/src/types'; export class CookieStorage implements KeyValueStorageInterface { path: string; @@ -18,19 +18,23 @@ export class CookieStorage implements KeyValueStorageInterface { constructor(data: CookieStorageData = {}) { const { path, domain, expires, sameSite, secure } = data; this.domain = domain; - this.path = path ? path : '/'; - this.expires = data.hasOwnProperty('expires') ? expires : 365; - this.secure = data.hasOwnProperty('secure') ? secure : true; + this.path = path || '/'; + this.expires = Object.prototype.hasOwnProperty.call(data, 'expires') + ? expires + : 365; + this.secure = Object.prototype.hasOwnProperty.call(data, 'secure') + ? secure + : true; - if (data.hasOwnProperty('sameSite')) { + if (Object.prototype.hasOwnProperty.call(data, 'sameSite')) { if (!sameSite || !['strict', 'lax', 'none'].includes(sameSite)) { throw new Error( - 'The sameSite value of cookieStorage must be "lax", "strict" or "none".' + 'The sameSite value of cookieStorage must be "lax", "strict" or "none".', ); } if (sameSite === 'none' && !this.secure) { throw new Error( - 'sameSite = None requires the Secure attribute in latest browser versions.' + 'sameSite = None requires the Secure attribute in latest browser versions.', ); } this.sameSite = sameSite; @@ -43,6 +47,7 @@ export class CookieStorage implements KeyValueStorageInterface { async getItem(key: string) { const item = JsCookie.get(key); + return item ?? null; } diff --git a/packages/core/src/storage/DefaultStorage.native.ts b/packages/core/src/storage/DefaultStorage.native.ts index 874261b0b1b..3147552358b 100644 --- a/packages/core/src/storage/DefaultStorage.native.ts +++ b/packages/core/src/storage/DefaultStorage.native.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAsyncStorage } from '@aws-amplify/react-native'; -import { KeyValueStorageInterface } from '../types'; +import { KeyValueStorageInterface } from '~/src/types'; const MEMORY_KEY_PREFIX = '@MemoryStorage:'; @@ -51,8 +51,9 @@ export class DefaultStorage implements KeyValueStorageInterface { */ async clear(): Promise { const allKeys = await this.asyncStorage.getAllKeys(); + return this.asyncStorage.multiRemove( - allKeys.filter(key => key.startsWith(MEMORY_KEY_PREFIX)) + allKeys.filter(key => key.startsWith(MEMORY_KEY_PREFIX)), ); } } diff --git a/packages/core/src/storage/InMemoryStorage.ts b/packages/core/src/storage/InMemoryStorage.ts index 58a0b3fb59f..14e14c75cdf 100644 --- a/packages/core/src/storage/InMemoryStorage.ts +++ b/packages/core/src/storage/InMemoryStorage.ts @@ -15,6 +15,7 @@ export class InMemoryStorage implements Storage { if (index > this.length - 1) { return null; } + return Array.from(this.storage.keys())[index]; } diff --git a/packages/core/src/storage/KeyValueStorage.ts b/packages/core/src/storage/KeyValueStorage.ts index 45290629774..661c699ebd9 100644 --- a/packages/core/src/storage/KeyValueStorage.ts +++ b/packages/core/src/storage/KeyValueStorage.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PlatformNotSupportedError } from '../errors'; -import { KeyValueStorageInterface } from '../types'; +import { PlatformNotSupportedError } from '~/src/errors'; +import { KeyValueStorageInterface } from '~/src/types'; /** * @internal @@ -33,6 +33,7 @@ export class KeyValueStorage implements KeyValueStorageInterface { */ async getItem(key: string) { if (!this.storage) throw new PlatformNotSupportedError(); + return this.storage.getItem(key); } diff --git a/packages/core/src/types/core.ts b/packages/core/src/types/core.ts index b66f11462e1..e1ec264f326 100644 --- a/packages/core/src/types/core.ts +++ b/packages/core/src/types/core.ts @@ -15,7 +15,7 @@ export interface AmplifyConfig { ssr?: boolean; } -export type UserProfile = { +export interface UserProfile { customProperties?: Record; demographic?: { appVersion?: string; @@ -39,7 +39,7 @@ export type UserProfile = { metrics?: Record; name?: string; plan?: string; -}; +} /** * @internal diff --git a/packages/core/src/types/errors.ts b/packages/core/src/types/errors.ts index 094f0a1e271..2e64193d85e 100644 --- a/packages/core/src/types/errors.ts +++ b/packages/core/src/types/errors.ts @@ -7,12 +7,12 @@ export enum AmplifyErrorCode { Unknown = 'Unknown', } -export type AmplifyErrorParams = { +export interface AmplifyErrorParams { message: string; name: ErrorCode; recoverySuggestion?: string; underlyingError?: Error | unknown; -}; +} export type AmplifyErrorMap = { [name in ErrorCode]: { @@ -21,10 +21,10 @@ export type AmplifyErrorMap = { }; }; -export type ServiceError = { +export interface ServiceError { name: string; message: string; -}; +} export type AssertionFunction = ( assertion: boolean, diff --git a/packages/core/src/types/storage.ts b/packages/core/src/types/storage.ts index 3a3c9774da6..d664a95b446 100644 --- a/packages/core/src/types/storage.ts +++ b/packages/core/src/types/storage.ts @@ -10,7 +10,7 @@ export interface KeyValueStorageInterface { export type SameSite = 'strict' | 'lax' | 'none'; -export type CookieStorageData = { +export interface CookieStorageData { domain?: string; path?: string; @@ -20,4 +20,4 @@ export type CookieStorageData = { expires?: number; secure?: boolean; sameSite?: SameSite; -}; +} diff --git a/packages/core/src/utils/WordArray.ts b/packages/core/src/utils/WordArray.ts index a69527ae9b8..653609887e3 100644 --- a/packages/core/src/utils/WordArray.ts +++ b/packages/core/src/utils/WordArray.ts @@ -12,8 +12,8 @@ import { cryptoSecureRandomInt } from './cryptoSecureRandomInt'; */ function hexStringify(wordArray: WordArray): string { // Shortcuts - const words = wordArray.words; - const sigBytes = wordArray.sigBytes; + const { words } = wordArray; + const { sigBytes } = wordArray; // Convert const hexChars: string[] = []; diff --git a/packages/core/src/utils/convert/base64/base64Decoder.ts b/packages/core/src/utils/convert/base64/base64Decoder.ts index 216e5fc5e5e..0a67290dc7f 100644 --- a/packages/core/src/utils/convert/base64/base64Decoder.ts +++ b/packages/core/src/utils/convert/base64/base64Decoder.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getAtob } from '../../globalHelpers'; -import { Base64Decoder } from '../types'; +import { getAtob } from '~/src/utils/globalHelpers'; +import { Base64Decoder } from '~/src/utils/convert/types'; export const base64Decoder: Base64Decoder = { convert(input) { diff --git a/packages/core/src/utils/convert/base64/base64Encoder.ts b/packages/core/src/utils/convert/base64/base64Encoder.ts index cf6adc83884..864a22a438d 100644 --- a/packages/core/src/utils/convert/base64/base64Encoder.ts +++ b/packages/core/src/utils/convert/base64/base64Encoder.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { getBtoa } from '../../globalHelpers'; -import { Base64Encoder } from '../types'; +import { getBtoa } from '~/src/utils/globalHelpers'; +import { Base64Encoder } from '~/src/utils/convert/types'; + import { bytesToString } from './bytesToString'; export const base64Encoder: Base64Encoder = { diff --git a/packages/core/src/utils/cryptoSecureRandomInt.ts b/packages/core/src/utils/cryptoSecureRandomInt.ts index 7a9e7fd13e4..86cbcbaa9dc 100644 --- a/packages/core/src/utils/cryptoSecureRandomInt.ts +++ b/packages/core/src/utils/cryptoSecureRandomInt.ts @@ -10,5 +10,6 @@ import { getCrypto } from './globalHelpers'; export function cryptoSecureRandomInt() { const crypto = getCrypto(); const randomResult = crypto.getRandomValues(new Uint32Array(1))[0]; + return randomResult; } diff --git a/packages/core/src/utils/generateRandomString.ts b/packages/core/src/utils/generateRandomString.ts index 7de257a0207..1610d3eeb26 100644 --- a/packages/core/src/utils/generateRandomString.ts +++ b/packages/core/src/utils/generateRandomString.ts @@ -8,7 +8,7 @@ export const generateRandomString = (length: number) => { for (let i = 0; i < length; i++) { result += STATE_CHARSET.charAt( - Math.floor(Math.random() * STATE_CHARSET.length) + Math.floor(Math.random() * STATE_CHARSET.length), ); } diff --git a/packages/core/src/utils/getClientInfo/getClientInfo.android.ts b/packages/core/src/utils/getClientInfo/getClientInfo.android.ts index 24626eef935..e7e22eb5d20 100644 --- a/packages/core/src/utils/getClientInfo/getClientInfo.android.ts +++ b/packages/core/src/utils/getClientInfo/getClientInfo.android.ts @@ -1,8 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 + +// TODO(eslint): remove this linter suppression. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: missing type definition -import { Platform, Dimensions } from 'react-native'; -import { ConsoleLogger } from '../../Logger'; +import { Dimensions, Platform } from 'react-native'; +import { ConsoleLogger } from '~/src/Logger'; const logger = new ConsoleLogger('getClientInfo'); diff --git a/packages/core/src/utils/getClientInfo/getClientInfo.ios.ts b/packages/core/src/utils/getClientInfo/getClientInfo.ios.ts index 215b63e1fec..b81ac68883b 100644 --- a/packages/core/src/utils/getClientInfo/getClientInfo.ios.ts +++ b/packages/core/src/utils/getClientInfo/getClientInfo.ios.ts @@ -1,8 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 + +// TODO(eslint): remove this linter suppression. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore: missing type definition -import { Platform, Dimensions } from 'react-native'; -import { ConsoleLogger } from '../../Logger'; +import { Dimensions, Platform } from 'react-native'; +import { ConsoleLogger } from '~/src/Logger'; const logger = new ConsoleLogger('getClientInfo'); @@ -12,6 +15,7 @@ export const getClientInfo = () => { const OS = 'ios'; const { Version } = Platform; const { make, model } = dimToMake(dim); + return { platform: OS, version: String(Version), diff --git a/packages/core/src/utils/getClientInfo/getClientInfo.ts b/packages/core/src/utils/getClientInfo/getClientInfo.ts index 90136330bc5..06535e65464 100644 --- a/packages/core/src/utils/getClientInfo/getClientInfo.ts +++ b/packages/core/src/utils/getClientInfo/getClientInfo.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '../../Logger'; +import { ConsoleLogger } from '~/src/Logger'; const logger = new ConsoleLogger('getClientInfo'); @@ -50,7 +50,7 @@ function browserTimezone() { function getBrowserType(userAgent: string) { // The latest user agents for Opera: https://www.whatismybrowser.com/guides/the-latest-user-agent/opera const operaMatch = /.+(Opera[\s[A-Z]*|OPR[\sA-Z]*)\/([0-9.]+).*/i.exec( - userAgent + userAgent, ); if (operaMatch) { return { type: operaMatch[1], version: operaMatch[2] }; @@ -58,7 +58,7 @@ function getBrowserType(userAgent: string) { // The latest user agents for Edge: https://www.whatismybrowser.com/guides/the-latest-user-agent/edge const ieMatch = /.+(Trident|Edge|Edg|EdgA|EdgiOS)\/([0-9.]+).*/i.exec( - userAgent + userAgent, ); if (ieMatch) { return { type: ieMatch[1], version: ieMatch[2] }; @@ -68,7 +68,7 @@ function getBrowserType(userAgent: string) { // https://www.whatismybrowser.com/guides/the-latest-user-agent/firefox // https://www.whatismybrowser.com/guides/the-latest-user-agent/chrome const cfMatch = /.+(Chrome|CriOS|Firefox|FxiOS)\/([0-9.]+).*/i.exec( - userAgent + userAgent, ); if (cfMatch) { return { type: cfMatch[1], version: cfMatch[2] }; diff --git a/packages/core/src/utils/globalHelpers/index.native.ts b/packages/core/src/utils/globalHelpers/index.native.ts index 8c0ca524792..7acb4d173ab 100644 --- a/packages/core/src/utils/globalHelpers/index.native.ts +++ b/packages/core/src/utils/globalHelpers/index.native.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { loadGetRandomValues, loadBase64 } from '@aws-amplify/react-native'; -import { AmplifyError } from '../../errors'; +import { loadBase64, loadGetRandomValues } from '@aws-amplify/react-native'; +import { AmplifyError } from '~/src/errors'; loadGetRandomValues(); const { encode, decode } = loadBase64(); diff --git a/packages/core/src/utils/globalHelpers/index.ts b/packages/core/src/utils/globalHelpers/index.ts index 622f4d3c3ef..49599165369 100644 --- a/packages/core/src/utils/globalHelpers/index.ts +++ b/packages/core/src/utils/globalHelpers/index.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AmplifyError } from '../../errors'; +import { AmplifyError } from '~/src/errors'; export const getCrypto = () => { if (typeof window === 'object' && typeof window.crypto === 'object') { diff --git a/packages/core/src/utils/isWebWorker.ts b/packages/core/src/utils/isWebWorker.ts index e39d84f50a0..184db958b3a 100644 --- a/packages/core/src/utils/isWebWorker.ts +++ b/packages/core/src/utils/isWebWorker.ts @@ -6,6 +6,7 @@ export const isWebWorker = () => { return false; } const selfContext = self as { WorkerGlobalScope?: any }; + return ( typeof selfContext.WorkerGlobalScope !== 'undefined' && self instanceof selfContext.WorkerGlobalScope diff --git a/packages/core/src/utils/retry/NonRetryableError.ts b/packages/core/src/utils/retry/NonRetryableError.ts index ed1a448030c..62f3fab496d 100644 --- a/packages/core/src/utils/retry/NonRetryableError.ts +++ b/packages/core/src/utils/retry/NonRetryableError.ts @@ -3,7 +3,4 @@ export class NonRetryableError extends Error { public readonly nonRetryable = true; - constructor(message: string) { - super(message); - } } diff --git a/packages/core/src/utils/retry/isNonRetryableError.ts b/packages/core/src/utils/retry/isNonRetryableError.ts index bc45e841b7e..8adcbd1ea3f 100644 --- a/packages/core/src/utils/retry/isNonRetryableError.ts +++ b/packages/core/src/utils/retry/isNonRetryableError.ts @@ -5,5 +5,6 @@ import { NonRetryableError } from './NonRetryableError'; export const isNonRetryableError = (obj: any): obj is NonRetryableError => { const key: keyof NonRetryableError = 'nonRetryable'; + return obj && obj[key]; }; diff --git a/packages/core/src/utils/retry/jitteredBackoff.ts b/packages/core/src/utils/retry/jitteredBackoff.ts index 95371e4f005..aae64ac9cd9 100644 --- a/packages/core/src/utils/retry/jitteredBackoff.ts +++ b/packages/core/src/utils/retry/jitteredBackoff.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { DelayFunction } from '../../types'; +import { DelayFunction } from '~/src/types'; + import { MAX_DELAY_MS } from './constants'; /** @@ -9,13 +10,14 @@ import { MAX_DELAY_MS } from './constants'; * Internal use of Amplify only */ export function jitteredBackoff( - maxDelayMs: number = MAX_DELAY_MS + maxDelayMs: number = MAX_DELAY_MS, ): DelayFunction { const BASE_TIME_MS = 100; const JITTER_FACTOR = 100; return attempt => { const delay = 2 ** attempt * BASE_TIME_MS + JITTER_FACTOR * Math.random(); + return delay > maxDelayMs ? false : delay; }; } diff --git a/packages/core/src/utils/retry/jitteredExponentialRetry.ts b/packages/core/src/utils/retry/jitteredExponentialRetry.ts index 16384793b23..59f28b96808 100644 --- a/packages/core/src/utils/retry/jitteredExponentialRetry.ts +++ b/packages/core/src/utils/retry/jitteredExponentialRetry.ts @@ -13,6 +13,6 @@ export const jitteredExponentialRetry = ( functionToRetry: (...args: any[]) => T, args: any[], maxDelayMs: number = MAX_DELAY_MS, - onTerminate?: Promise + onTerminate?: Promise, ): Promise => retry(functionToRetry, args, jitteredBackoff(maxDelayMs), onTerminate); diff --git a/packages/core/src/utils/retry/retry.ts b/packages/core/src/utils/retry/retry.ts index dfa27f51c1d..9fa89b98327 100644 --- a/packages/core/src/utils/retry/retry.ts +++ b/packages/core/src/utils/retry/retry.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { DelayFunction } from '../../types'; -import { ConsoleLogger } from '../../Logger/ConsoleLogger'; +import { DelayFunction } from '~/src/types'; +import { ConsoleLogger } from '~/src/Logger/ConsoleLogger'; + import { isNonRetryableError } from './isNonRetryableError'; const logger = new ConsoleLogger('retryUtil'); @@ -15,17 +16,21 @@ export async function retry( functionToRetry: (...args: any[]) => T, args: any[], delayFn: DelayFunction, - onTerminate?: Promise + onTerminate?: Promise, ): Promise { if (typeof functionToRetry !== 'function') { throw Error('functionToRetry must be a function'); } + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { let attempt = 0; let terminated = false; let timeout: any; - let wakeUp: any = () => {}; // will be replaced with a resolver() + let wakeUp: any = () => { + // no-op + }; // will be replaced with a resolver() // used after the loop if terminated while waiting for a timer. let lastError: unknown; @@ -40,24 +45,31 @@ export async function retry( wakeUp(); }); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-unmodified-loop-condition while (!terminated) { attempt++; logger.debug( `${ functionToRetry.name - } attempt #${attempt} with this vars: ${JSON.stringify(args)}` + } attempt #${attempt} with this vars: ${JSON.stringify(args)}`, ); try { - return resolve(await functionToRetry(...args)); + resolve(await functionToRetry(...args)); + + return; } catch (err) { lastError = err; logger.debug(`error on ${functionToRetry.name}`, err); if (isNonRetryableError(err)) { logger.debug(`${functionToRetry.name} non retryable error`, err); - return reject(err); + + reject(err); + + return; } const retryIn = delayFn(attempt, args, err); @@ -66,10 +78,12 @@ export async function retry( // we check `terminated` again here because it could have flipped // in the time it took `functionToRetry` to return. if (retryIn === false || terminated) { - return reject(err); + reject(err); + + return; } else { - await new Promise(r => { - wakeUp = r; // export wakeUp for onTerminate handling + await new Promise(_resolve => { + wakeUp = _resolve; // export wakeUp for onTerminate handling timeout = setTimeout(wakeUp, retryIn); }); } diff --git a/packages/core/src/utils/sessionListener/SessionListener.native.ts b/packages/core/src/utils/sessionListener/SessionListener.native.ts index 41da01b671c..507b1a69f21 100644 --- a/packages/core/src/utils/sessionListener/SessionListener.native.ts +++ b/packages/core/src/utils/sessionListener/SessionListener.native.ts @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAppState } from '@aws-amplify/react-native'; + import { - SessionStateChangeListener, - SessionState, SessionListenerInterface, + SessionState, + SessionStateChangeListener, } from './types'; const stateChangeListeners = new Set(); @@ -25,7 +26,7 @@ export class SessionListener implements SessionListenerInterface { public addStateChangeListener( listener: SessionStateChangeListener, - notifyOnAdd: boolean = false + notifyOnAdd = false, ) { stateChangeListeners.add(listener); diff --git a/packages/core/src/utils/sessionListener/SessionListener.ts b/packages/core/src/utils/sessionListener/SessionListener.ts index caa8a0d8147..cb11ba09c65 100644 --- a/packages/core/src/utils/sessionListener/SessionListener.ts +++ b/packages/core/src/utils/sessionListener/SessionListener.ts @@ -1,8 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { isBrowser } from '../isBrowser'; -import { SessionStateChangeListener, SessionListenerInterface } from './types'; +import { isBrowser } from '~/src/utils/isBrowser'; + +import { SessionListenerInterface, SessionStateChangeListener } from './types'; const stateChangeListeners = new Set(); @@ -18,7 +19,7 @@ export class SessionListener implements SessionListenerInterface { document.addEventListener( 'visibilitychange', this.handleVisibilityChange, - false + false, ); this.listenerActive = true; @@ -27,7 +28,7 @@ export class SessionListener implements SessionListenerInterface { public addStateChangeListener( listener: SessionStateChangeListener, - notifyOnAdd: boolean = false + notifyOnAdd = false, ) { // No-op if document listener is not active if (!this.listenerActive) { diff --git a/packages/core/src/utils/sessionListener/index.ts b/packages/core/src/utils/sessionListener/index.ts index 0b89104e3d0..8b41db28055 100644 --- a/packages/core/src/utils/sessionListener/index.ts +++ b/packages/core/src/utils/sessionListener/index.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { SessionListener } from './SessionListener'; + export { SESSION_START_EVENT, SESSION_STOP_EVENT } from './constants'; export const sessionListener = new SessionListener(); diff --git a/packages/core/src/utils/sessionListener/types.ts b/packages/core/src/utils/sessionListener/types.ts index 8bede49b259..eabc078432a 100644 --- a/packages/core/src/utils/sessionListener/types.ts +++ b/packages/core/src/utils/sessionListener/types.ts @@ -6,6 +6,6 @@ export type SessionState = 'started' | 'ended'; export type SessionStateChangeListener = (state: SessionState) => void; export interface SessionListenerInterface { - addStateChangeListener: (listener: SessionStateChangeListener) => void; - removeStateChangeListener: (listener: SessionStateChangeListener) => void; + addStateChangeListener(listener: SessionStateChangeListener): void; + removeStateChangeListener(listener: SessionStateChangeListener): void; } diff --git a/packages/core/src/utils/urlSafeDecode.ts b/packages/core/src/utils/urlSafeDecode.ts index df0154a5dd1..3be073c9b32 100644 --- a/packages/core/src/utils/urlSafeDecode.ts +++ b/packages/core/src/utils/urlSafeDecode.ts @@ -3,5 +3,6 @@ export function urlSafeDecode(hex: string) { const matchArr = hex.match(/.{2}/g) || []; + return matchArr.map(char => String.fromCharCode(parseInt(char, 16))).join(''); } diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/core/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 22343ad4f8b..ad38de94057 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -1,12 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { + "baseUrl": ".", "importHelpers": false, - "strict": true, - "noImplicitAny": true + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"], - "watchOptions": { - "excludeDirectories": ["lib*"] - } + "include": ["./src", "__tests__"] } diff --git a/packages/core/tsconfig.test.json b/packages/core/tsconfig.test.json new file mode 100644 index 00000000000..f08eadc99db --- /dev/null +++ b/packages/core/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false, + "types": ["node", "jest", "jsdom"] + } +} diff --git a/packages/core/tsconfig.watch.json b/packages/core/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/core/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/core/tslint.json b/packages/core/tslint.json deleted file mode 100644 index f4f64297075..00000000000 --- a/packages/core/tslint.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [ - true, - { - "ignore-pattern": "//", - "limit": 120 - } - ], - "no-empty-interface": false, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/datastore-storage-adapter/.eslintrc.js b/packages/datastore-storage-adapter/.eslintrc.js new file mode 100644 index 00000000000..38d5c8b1aee --- /dev/null +++ b/packages/datastore-storage-adapter/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/datastore-storage-adapter', + }, + ], + }, +}; diff --git a/packages/datastore-storage-adapter/__tests__/SQLiteAdapter.test.ts b/packages/datastore-storage-adapter/__tests__/SQLiteAdapter.test.ts index 4e9fb67b497..58c18a07f90 100644 --- a/packages/datastore-storage-adapter/__tests__/SQLiteAdapter.test.ts +++ b/packages/datastore-storage-adapter/__tests__/SQLiteAdapter.test.ts @@ -23,6 +23,7 @@ import { let innerSQLiteDatabase; +jest.mock('../../datastore/src/storage/relationship.ts'); jest.mock('@aws-amplify/datastore/src/sync/datastoreConnectivity', () => { return { status: () => of(false) as any, @@ -168,7 +169,7 @@ describe('SQLiteAdapter', () => { null!, null!, null!, - ] + ], ); const queries = new Set(); diff --git a/packages/datastore-storage-adapter/jest.config.js b/packages/datastore-storage-adapter/jest.config.js deleted file mode 100644 index cd3e90fab78..00000000000 --- a/packages/datastore-storage-adapter/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 82, - functions: 97, - lines: 88, - statements: 88, - }, - }, -}; diff --git a/packages/datastore-storage-adapter/jest.config.mjs b/packages/datastore-storage-adapter/jest.config.mjs new file mode 100644 index 00000000000..d5fa2fa4ad4 --- /dev/null +++ b/packages/datastore-storage-adapter/jest.config.mjs @@ -0,0 +1,18 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.test.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 61, + functions: 71, + lines: 65, + statements: 65, + }, + }, + moduleNameMapper: { + '/^~/(.*)$/': '/$1', + }, +}); + +export default jestConfig; diff --git a/packages/datastore-storage-adapter/package.json b/packages/datastore-storage-adapter/package.json index 6db674d45e9..cf98d46942f 100644 --- a/packages/datastore-storage-adapter/package.json +++ b/packages/datastore-storage-adapter/package.json @@ -10,17 +10,18 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "rimraf dist lib lib-esm", "format": "echo \"Not implemented\"", - "lint": "tslint '{__tests__,src}/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 94.16" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 94.16" }, "repository": { "type": "git", @@ -44,7 +45,6 @@ "expo-sqlite": "10.1.0", "react-native-sqlite-storage": "5.0.0", "rollup": "3.29.4", - "sqlite3": "^5.0.2", - "typescript": "5.0.2" + "sqlite3": "^5.0.2" } } diff --git a/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteAdapter.ts b/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteAdapter.ts index 64c6eb6d83a..5912492703b 100644 --- a/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteAdapter.ts +++ b/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteAdapter.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { CommonSQLiteAdapter } from '../common/CommonSQLiteAdapter'; +import { CommonSQLiteAdapter } from '~/src/common/CommonSQLiteAdapter'; + import ExpoSQLiteDatabase from './ExpoSQLiteDatabase'; const ExpoSQLiteAdapter: CommonSQLiteAdapter = new CommonSQLiteAdapter( - new ExpoSQLiteDatabase() + new ExpoSQLiteDatabase(), ); export default ExpoSQLiteAdapter; diff --git a/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteDatabase.ts b/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteDatabase.ts index bfc6a18b48a..0f2f2d040ee 100644 --- a/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteDatabase.ts +++ b/packages/datastore-storage-adapter/src/ExpoSQLiteAdapter/ExpoSQLiteDatabase.ts @@ -3,17 +3,20 @@ import { ConsoleLogger } from '@aws-amplify/core'; import { PersistentModel } from '@aws-amplify/datastore'; import { deleteAsync, documentDirectory } from 'expo-file-system'; -import { openDatabase, WebSQLDatabase } from 'expo-sqlite'; -import { DB_NAME } from '../common/constants'; -import { CommonSQLiteDatabase, ParameterizedStatement } from '../common/types'; +import { WebSQLDatabase, openDatabase } from 'expo-sqlite'; +import { DB_NAME } from '~/src/common/constants'; +import { + CommonSQLiteDatabase, + ParameterizedStatement, +} from '~/src/common/types'; const logger = new ConsoleLogger('ExpoSQLiteDatabase'); /* -Note: -ExpoSQLite transaction error callbacks require returning a boolean value to indicate whether the -error was handled or not. Returning a true value indicates the error was handled and does not +Note: +ExpoSQLite transaction error callbacks require returning a boolean value to indicate whether the +error was handled or not. Returning a true value indicates the error was handled and does not rollback the whole transaction. */ @@ -53,15 +56,16 @@ class ExpoSQLiteDatabase implements CommonSQLiteDatabase { public async get( statement: string, - params: (string | number)[] + params: (string | number)[], ): Promise { const results: T[] = await this.getAll(statement, params); + return results[0]; } public getAll( statement: string, - params: (string | number)[] + params: (string | number)[], ): Promise { return new Promise((resolve, reject) => { this.db.readTransaction(transaction => { @@ -74,8 +78,9 @@ class ExpoSQLiteDatabase implements CommonSQLiteDatabase { (_, error) => { reject(error); logger.warn(error); + return true; - } + }, ); }); }); @@ -93,41 +98,43 @@ class ExpoSQLiteDatabase implements CommonSQLiteDatabase { (_, error) => { reject(error); logger.warn(error); + return true; - } + }, ); }); }); } public batchQuery( - queryParameterizedStatements: Set = new Set() + queryParameterizedStatements = new Set(), ): Promise { - return new Promise((resolveTransaction, rejectTransaction) => { + return new Promise((resolve, reject) => { this.db.transaction(async transaction => { try { const results: any[] = await Promise.all( [...queryParameterizedStatements].map( ([statement, params]) => - new Promise((resolve, reject) => { + new Promise((_resolve, _reject) => { transaction.executeSql( statement, params, (_, result) => { - resolve(result.rows._array[0]); + _resolve(result.rows._array[0]); }, (_, error) => { - reject(error); + _reject(error); logger.warn(error); + return true; - } + }, ); - }) - ) + }), + ), ); - resolveTransaction(results); + resolve(results); } catch (error) { - rejectTransaction(error); + reject(error); logger.warn(error); } }); @@ -135,56 +142,58 @@ class ExpoSQLiteDatabase implements CommonSQLiteDatabase { } public batchSave( - saveParameterizedStatements: Set = new Set(), - deleteParameterizedStatements?: Set + saveParameterizedStatements = new Set(), + deleteParameterizedStatements?: Set, ): Promise { - return new Promise((resolveTransaction, rejectTransaction) => { + return new Promise((resolve, reject) => { this.db.transaction(async transaction => { try { // await for all sql statements promises to resolve await Promise.all( [...saveParameterizedStatements].map( ([statement, params]) => - new Promise((resolve, reject) => { + new Promise((_resolve, _reject) => { transaction.executeSql( statement, params, () => { - resolve(null); + _resolve(null); }, (_, error) => { - reject(error); + _reject(error); logger.warn(error); + return true; - } + }, ); - }) - ) + }), + ), ); if (deleteParameterizedStatements) { await Promise.all( [...deleteParameterizedStatements].map( ([statement, params]) => - new Promise((resolve, reject) => + new Promise((_resolve, _reject) => { transaction.executeSql( statement, params, () => { - resolve(null); + _resolve(null); }, (_, error) => { - reject(error); + _reject(error); logger.warn(error); + return true; - } - ) - ) - ) + }, + ); + }), + ), ); } - resolveTransaction(null); + resolve(null); } catch (error) { - rejectTransaction(error); + reject(error); logger.warn(error); } }); @@ -193,45 +202,47 @@ class ExpoSQLiteDatabase implements CommonSQLiteDatabase { public selectAndDelete( queryParameterizedStatement: ParameterizedStatement, - deleteParameterizedStatement: ParameterizedStatement + deleteParameterizedStatement: ParameterizedStatement, ): Promise { const [queryStatement, queryParams] = queryParameterizedStatement; const [deleteStatement, deleteParams] = deleteParameterizedStatement; - return new Promise((resolveTransaction, rejectTransaction) => { + return new Promise((resolve, reject) => { this.db.transaction(async transaction => { try { - const result: T[] = await new Promise((resolve, reject) => { + const result: T[] = await new Promise((_resolve, _reject) => { transaction.executeSql( queryStatement, queryParams, - (_, result) => { - resolve(result.rows._array || []); + (_, executeSqlResult) => { + _resolve(executeSqlResult.rows._array || []); }, (_, error) => { - reject(error); + _reject(error); logger.warn(error); + return true; - } + }, ); }); - await new Promise((resolve, reject) => { + await new Promise((_resolve, _reject) => { transaction.executeSql( deleteStatement, deleteParams, () => { - resolve(null); + _resolve(null); }, (_, error) => { - reject(error); + _reject(error); logger.warn(error); + return true; - } + }, ); }); - resolveTransaction(result); + resolve(result); } catch (error) { - rejectTransaction(error); + reject(error); logger.warn(error); } }); @@ -239,30 +250,31 @@ class ExpoSQLiteDatabase implements CommonSQLiteDatabase { } private executeStatements(statements: string[]): Promise { - return new Promise((resolveTransaction, rejectTransaction) => { + return new Promise((resolve, reject) => { this.db.transaction(async transaction => { try { await Promise.all( statements.map( statement => - new Promise((resolve, reject) => { + new Promise((_resolve, _reject) => { transaction.executeSql( statement, [], () => { - resolve(null); + _resolve(null); }, (_, error) => { - reject(error); + _reject(error); + return true; - } + }, ); - }) - ) + }), + ), ); - resolveTransaction(null); + resolve(null); } catch (error) { - rejectTransaction(error); + reject(error); logger.warn(error); } }); diff --git a/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteAdapter.ts b/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteAdapter.ts index 6e751b85446..dbee3c754bf 100644 --- a/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteAdapter.ts +++ b/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteAdapter.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { CommonSQLiteAdapter } from '../common/CommonSQLiteAdapter'; +import { CommonSQLiteAdapter } from '~/src/common/CommonSQLiteAdapter'; + import SQLiteDatabase from './SQLiteDatabase'; const SQLiteAdapter: CommonSQLiteAdapter = new CommonSQLiteAdapter( - new SQLiteDatabase() + new SQLiteDatabase(), ); export default SQLiteAdapter; diff --git a/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteDatabase.ts b/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteDatabase.ts index 103dcccfa1e..62eea46e4a7 100644 --- a/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteDatabase.ts +++ b/packages/datastore-storage-adapter/src/SQLiteAdapter/SQLiteDatabase.ts @@ -3,8 +3,11 @@ import SQLite from 'react-native-sqlite-storage'; import { ConsoleLogger } from '@aws-amplify/core'; import { PersistentModel } from '@aws-amplify/datastore'; -import { DB_NAME } from '../common/constants'; -import { CommonSQLiteDatabase, ParameterizedStatement } from '../common/types'; +import { DB_NAME } from '~/src/common/constants'; +import { + CommonSQLiteDatabase, + ParameterizedStatement, +} from '~/src/common/types'; const logger = new ConsoleLogger('SQLiteDatabase'); @@ -16,7 +19,7 @@ if (ConsoleLogger.LOG_LEVEL === 'DEBUG') { /* -Note: +Note: I purposely avoided using arrow functions () => {} in this class, Because I ran into issues with them in some of the SQLite method callbacks @@ -41,7 +44,7 @@ class SQLiteDatabase implements CommonSQLiteDatabase { } public async createSchema(statements: string[]): Promise { - return await this.executeStatements(statements); + await this.executeStatements(statements); } public async clear(): Promise { @@ -53,15 +56,16 @@ class SQLiteDatabase implements CommonSQLiteDatabase { public async get( statement: string, - params: (string | number)[] + params: (string | number)[], ): Promise { const results: T[] = await this.getAll(statement, params); + return results[0]; } public async getAll( statement: string, - params: (string | number)[] + params: (string | number)[], ): Promise { const [resultSet] = await this.db.executeSql(statement, params); const result = @@ -76,13 +80,13 @@ class SQLiteDatabase implements CommonSQLiteDatabase { public async save( statement: string, - params: (string | number)[] + params: (string | number)[], ): Promise { await this.db.executeSql(statement, params); } public async batchQuery( - queryParameterizedStatements: Set + queryParameterizedStatements: Set, ): Promise { const results = []; @@ -94,7 +98,7 @@ class SQLiteDatabase implements CommonSQLiteDatabase { (_, res) => { results.push(res.rows.raw()[0]); }, - logger.warn + logger.warn, ); } }); @@ -104,7 +108,7 @@ class SQLiteDatabase implements CommonSQLiteDatabase { public async batchSave( saveParameterizedStatements: Set, - deleteParameterizedStatements?: Set + deleteParameterizedStatements?: Set, ): Promise { await this.db.transaction(tx => { for (const [statement, params] of saveParameterizedStatements) { @@ -122,7 +126,7 @@ class SQLiteDatabase implements CommonSQLiteDatabase { public async selectAndDelete( queryParameterizedStatement: ParameterizedStatement, - deleteParameterizedStatement: ParameterizedStatement + deleteParameterizedStatement: ParameterizedStatement, ): Promise { let results: T[] = []; @@ -136,9 +140,16 @@ class SQLiteDatabase implements CommonSQLiteDatabase { (_, res) => { results = res.rows.raw(); }, - logger.warn + logger.warn, + ); + tx.executeSql( + deleteStatement, + deleteParams, + () => { + // no-op + }, + logger.warn, ); - tx.executeSql(deleteStatement, deleteParams, () => {}, logger.warn); }); return results; diff --git a/packages/datastore-storage-adapter/src/common/CommonSQLiteAdapter.ts b/packages/datastore-storage-adapter/src/common/CommonSQLiteAdapter.ts index a6ccf3a97c2..166384f461c 100644 --- a/packages/datastore-storage-adapter/src/common/CommonSQLiteAdapter.ts +++ b/packages/datastore-storage-adapter/src/common/CommonSQLiteAdapter.ts @@ -2,26 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 import { ConsoleLogger } from '@aws-amplify/core'; import { + deleteByIdStatement, + deleteByPredicateStatement, generateSchemaStatements, - queryByIdStatement, - modelUpdateStatement, modelInsertStatement, + modelUpdateStatement, queryAllStatement, + queryByIdStatement, queryOneStatement, - deleteByIdStatement, - deleteByPredicateStatement, -} from '../common/SQLiteUtils'; - +} from '~/src/common/SQLiteUtils'; import { - StorageAdapter, + InternalSchema, ModelInstanceCreator, + ModelPredicate, ModelPredicateCreator, ModelSortPredicateCreator, - InternalSchema, - isPredicateObj, - ModelPredicate, - NamespaceResolver, NAMESPACES, + NamespaceResolver, OpType, PaginationInput, PersistentModel, @@ -29,12 +26,15 @@ import { PredicateObject, PredicatesGroup, QueryOne, + StorageAdapter, + isPredicateObj, utils, } from '@aws-amplify/datastore'; + import { CommonSQLiteDatabase, - ParameterizedStatement, ModelInstanceMetadataWithId, + ParameterizedStatement, } from './types'; const { traverseModel, validatePredicate, isModelConstructor } = utils; @@ -47,8 +47,9 @@ export class CommonSQLiteAdapter implements StorageAdapter { private modelInstanceCreator: ModelInstanceCreator; private getModelConstructorByModelName: ( namsespaceName: string, - modelName: string + modelName: string, ) => PersistentModelConstructor; + private db: CommonSQLiteDatabase; private initPromise: Promise; private resolve: (value?: any) => void; @@ -64,16 +65,17 @@ export class CommonSQLiteAdapter implements StorageAdapter { modelInstanceCreator: ModelInstanceCreator, getModelConstructorByModelName: ( namsespaceName: NAMESPACES, - modelName: string - ) => PersistentModelConstructor + modelName: string, + ) => PersistentModelConstructor, ) { if (!this.initPromise) { - this.initPromise = new Promise((res, rej) => { - this.resolve = res; - this.reject = rej; + this.initPromise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; }); } else { await this.initPromise; + return; } this.schema = theSchema; @@ -83,15 +85,20 @@ export class CommonSQLiteAdapter implements StorageAdapter { try { const usesCPKCodegen = Object.values( - this.schema.namespaces.user.models + this.schema.namespaces.user.models, ).some(model => Object.values(model.fields).some( - field => field.association?.hasOwnProperty('targetNames') - ) + field => + field.association && + Object.prototype.hasOwnProperty.call( + field.association, + 'targetNames', + ), + ), ); if (usesCPKCodegen) { logger.error( - 'The SQLite adapter does not support schemas using custom primary key. Set `graphQLTransformer.respectPrimaryKeyAttributesOnConnectionField in `amplify/cli.json` to false to disable custom primary key. To regenerate your API, add or remove an empty newline to your GraphQL schema (to change the computed hash) then run `amplify push`.' + 'The SQLite adapter does not support schemas using custom primary key. Set `graphQLTransformer.respectPrimaryKeyAttributesOnConnectionField in `amplify/cli.json` to false to disable custom primary key. To regenerate your API, add or remove an empty newline to your GraphQL schema (to change the computed hash) then run `amplify push`.', ); } await this.db.init(); @@ -112,7 +119,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { async save( model: T, - condition?: ModelPredicate + condition?: ModelPredicate, ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> { const modelConstructor = Object.getPrototypeOf(model) .constructor as PersistentModelConstructor; @@ -122,12 +129,12 @@ export class CommonSQLiteAdapter implements StorageAdapter { model, this.schema.namespaces[this.namespaceResolver(modelConstructor)], this.modelInstanceCreator, - this.getModelConstructorByModelName + this.getModelConstructorByModelName, ); const connectionStoreNames = Object.values(connectedModels).map( ({ modelName, item, instance }) => { return { modelName, item, instance }; - } + }, ); const [queryStatement, params] = queryByIdStatement(model.id, tableName); @@ -155,13 +162,19 @@ export class CommonSQLiteAdapter implements StorageAdapter { const { modelName, item, instance } = resItem; const { id } = item; - const [queryStatement, params] = queryByIdStatement(id, modelName); - const fromDB = await this.db.get(queryStatement, params); + const [resItemQueryStatement, resItemParams] = queryByIdStatement( + id, + modelName, + ); + const fromDBForResItem = await this.db.get( + resItemQueryStatement, + resItemParams, + ); const opType: OpType = - fromDB === undefined ? OpType.INSERT : OpType.UPDATE; + fromDBForResItem === undefined ? OpType.INSERT : OpType.UPDATE; - const saveStatement = fromDB + const saveStatement = fromDBForResItem ? modelUpdateStatement(instance, modelName) : modelInsertStatement(instance, modelName); @@ -179,7 +192,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { private async load( namespaceName: string, srcModelName: string, - records: T[] + records: T[], ): Promise { const namespace = this.schema.namespaces[namespaceName]; const relations = namespace.relationships[srcModelName].relationTypes; @@ -187,12 +200,12 @@ export class CommonSQLiteAdapter implements StorageAdapter { const modelConstructor = this.getModelConstructorByModelName( namespaceName, - srcModelName + srcModelName, ); if (connectionTableNames.length === 0) { return records.map(record => - this.modelInstanceCreator(modelConstructor, record) + this.modelInstanceCreator(modelConstructor, record), ); } @@ -205,6 +218,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { for (const r of relations) { delete record[r.fieldName]; } + return this.modelInstanceCreator(modelConstructor, record); }); } @@ -212,7 +226,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { async query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { const { name: tableName } = modelConstructor; const namespaceName = this.namespaceResolver(modelConstructor); @@ -228,9 +242,10 @@ export class CommonSQLiteAdapter implements StorageAdapter { const queryById = predicates && this.idFromPredicate(predicates); - const records: T[] = await (async () => { + const records: T[] = (await (async () => { if (queryById) { const record = await this.getById(tableName, queryById); + return record ? [record] : []; } @@ -239,18 +254,18 @@ export class CommonSQLiteAdapter implements StorageAdapter { predicates, sortPredicates, limit, - page + page, ); - return await this.db.getAll(queryStatement, params); - })(); + return this.db.getAll(queryStatement, params); + })()) as T[]; - return await this.load(namespaceName, modelConstructor.name, records); + return this.load(namespaceName, modelConstructor.name, records); } private async getById( tableName: string, - id: string + id: string, ): Promise { const [queryStatement, params] = queryByIdStatement(id, tableName); const record = await this.db.get(queryStatement, params); @@ -259,13 +274,13 @@ export class CommonSQLiteAdapter implements StorageAdapter { } private idFromPredicate( - predicates: PredicatesGroup + predicates: PredicatesGroup, ) { const { predicates: predicateObjs } = predicates; const idPredicate = predicateObjs.length === 1 && (predicateObjs.find( - p => isPredicateObj(p) && p.field === 'id' && p.operator === 'eq' + p => isPredicateObj(p) && p.field === 'id' && p.operator === 'eq', ) as PredicateObject); return idPredicate && idPredicate.operand; @@ -273,7 +288,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { async queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne = QueryOne.FIRST + firstOrLast: QueryOne = QueryOne.FIRST, ): Promise { const { name: tableName } = modelConstructor; const [queryStatement, params] = queryOneStatement(firstOrLast, tableName); @@ -291,7 +306,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { // For Has Many and Has One relations to have SQL handle cascades automatically async delete( modelOrModelConstructor: T | PersistentModelConstructor, - condition?: ModelPredicate + condition?: ModelPredicate, ): Promise<[T[], T[]]> { if (isModelConstructor(modelOrModelConstructor)) { const modelConstructor = modelOrModelConstructor; @@ -306,13 +321,13 @@ export class CommonSQLiteAdapter implements StorageAdapter { const models = await this.db.selectAndDelete( queryStatement, - deleteStatement + deleteStatement, ); const modelInstances = await this.load( namespaceName, modelConstructor.name, - models + models, ); return [modelInstances, modelInstances]; @@ -325,7 +340,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { if (condition) { const [queryStatement, params] = queryByIdStatement( model.id, - tableName + tableName, ); const fromDB = await this.db.get(queryStatement, params); @@ -351,7 +366,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { const [deleteStatement, deleteParams] = deleteByIdStatement( model.id, - tableName + tableName, ); await this.db.save(deleteStatement, deleteParams); @@ -359,7 +374,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { } else { const [deleteStatement, params] = deleteByIdStatement( model.id, - tableName + tableName, ); await this.db.save(deleteStatement, params); @@ -370,7 +385,7 @@ export class CommonSQLiteAdapter implements StorageAdapter { async batchSave( modelConstructor: PersistentModelConstructor, - items: ModelInstanceMetadataWithId[] + items: ModelInstanceMetadataWithId[], ): Promise<[T, OpType][]> { const { name: tableName } = modelConstructor; const result: [T, OpType][] = []; @@ -390,20 +405,21 @@ export class CommonSQLiteAdapter implements StorageAdapter { this.modelInstanceCreator(modelConstructor, item), this.schema.namespaces[this.namespaceResolver(modelConstructor)], this.modelInstanceCreator, - this.getModelConstructorByModelName + this.getModelConstructorByModelName, ); const { id, _deleted } = item; const { instance } = connectedModels.find( - ({ instance }) => instance.id === id + ({ instance: instanceOfConnectedModel }) => + instanceOfConnectedModel.id === id, ); if (_deleted) { // create the delete statements right away const deleteStatement = deleteByIdStatement(instance.id, tableName); deleteStatements.add(deleteStatement); - result.push([(item), OpType.DELETE]); + result.push([item as unknown as T, OpType.DELETE]); } else { // query statements for the saves at first const queryStatement = queryByIdStatement(id, tableName); @@ -420,17 +436,17 @@ export class CommonSQLiteAdapter implements StorageAdapter { if (response === undefined) { const insertStatement = modelInsertStatement( itemsToSave[idx], - tableName + tableName, ); saveStatements.add(insertStatement); - result.push([(itemsToSave[idx]), OpType.INSERT]); + result.push([itemsToSave[idx] as unknown as T, OpType.INSERT]); } else { const updateStatement = modelUpdateStatement( itemsToSave[idx], - tableName + tableName, ); saveStatements.add(updateStatement); - result.push([(itemsToSave[idx]), OpType.UPDATE]); + result.push([itemsToSave[idx] as unknown as T, OpType.UPDATE]); } }); diff --git a/packages/datastore-storage-adapter/src/common/SQLiteUtils.ts b/packages/datastore-storage-adapter/src/common/SQLiteUtils.ts index e1434c767c3..19dc588a4fb 100644 --- a/packages/datastore-storage-adapter/src/common/SQLiteUtils.ts +++ b/packages/datastore-storage-adapter/src/common/SQLiteUtils.ts @@ -1,24 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { + GraphQLScalarType, InternalSchema, - SchemaModel, + ModelAttributeAuth, + ModelAuthRule, ModelField, PersistentModel, - isGraphQLScalarType, - QueryOne, + PredicateObject, PredicatesGroup, - isPredicateObj, + QueryOne, + SchemaModel, SortPredicatesGroup, - PredicateObject, - isPredicateGroup, + isGraphQLScalarType, + isModelAttributeAuth, isModelFieldType, + isPredicateGroup, + isPredicateObj, isTargetNameAssociation, - isModelAttributeAuth, - ModelAttributeAuth, - ModelAuthRule, utils, - GraphQLScalarType, } from '@aws-amplify/datastore'; import { ParameterizedStatement } from './types'; @@ -43,6 +43,7 @@ const updateSet: (model: any) => [any, any] = model => { .filter(([k]) => k !== 'id') .map(([k, v]) => { values.push(prepareValueForDML(v)); + return `"${k}"=?`; }) .join(', '); @@ -77,7 +78,7 @@ export function getSQLiteType( scalar: keyof Omit< typeof GraphQLScalarType, 'getJSType' | 'getValidationFunction' | 'getSQLiteType' - > + >, ): 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' { switch (scalar) { case 'Boolean': @@ -97,9 +98,10 @@ export function getSQLiteType( return 'TEXT'; case 'Float': return 'REAL'; - default: + default: { const _: never = scalar as never; throw new Error(`unknown type ${scalar as string}`); + } } } @@ -109,13 +111,13 @@ export function generateSchemaStatements(schema: InternalSchema): string[] { const isUserModel = namespaceName === USER; return Object.values(namespace.models).map(model => - modelCreateTableStatement(model, isUserModel) + modelCreateTableStatement(model, isUserModel), ); }); } export const implicitAuthFieldsForModel: (model: SchemaModel) => string[] = ( - model: SchemaModel + model: SchemaModel, ) => { if (!model.attributes || !model.attributes.length) { return []; @@ -134,15 +136,16 @@ export const implicitAuthFieldsForModel: (model: SchemaModel) => string[] = ( return authFieldsForModel.filter((authField: string) => { const authFieldExplicitlyDefined = Object.values(model.fields).find( - (f: ModelField) => f.name === authField + (f: ModelField) => f.name === authField, ); + return !authFieldExplicitlyDefined; }); }; export function modelCreateTableStatement( model: SchemaModel, - userModel: boolean = false + userModel = false, ): string { // implicitly defined auth fields, e.g., `owner`, `groupsField`, etc. const implicitAuthFields = implicitAuthFieldsForModel(model); @@ -169,7 +172,7 @@ export function modelCreateTableStatement( if (isTargetNameAssociation(field.association)) { // check if this field has been explicitly defined in the model const fkDefinedInModel = Object.values(model.fields).find( - (f: ModelField) => f.name === field?.association?.targetName + (f: ModelField) => f.name === field?.association?.targetName, ); // if the FK is not explicitly defined in the model, we have to add it here @@ -210,12 +213,13 @@ export function modelCreateTableStatement( const createTableStatement = `CREATE TABLE IF NOT EXISTS "${ model.name }" (${fields.join(', ')});`; + return createTableStatement; } export function modelInsertStatement( model: PersistentModel, - tableName: string + tableName: string, ): ParameterizedStatement { const keys = keysFromModel(model); const [paramaterized, values] = valuesFromModel(model); @@ -227,7 +231,7 @@ export function modelInsertStatement( export function modelUpdateStatement( model: PersistentModel, - tableName: string + tableName: string, ): ParameterizedStatement { const [paramaterized, values] = updateSet(model); @@ -238,7 +242,7 @@ export function modelUpdateStatement( export function queryByIdStatement( id: string, - tableName: string + tableName: string, ): ParameterizedStatement { return [`SELECT * FROM "${tableName}" WHERE "id" = ?`, [id]]; } @@ -305,7 +309,7 @@ export const whereConditionFromPredicateObject = ({ const specialNullClause = buildSpecialNullComparison( field, operator, - operand + operand, ); if (specialNullClause) { return [specialNullClause, []]; @@ -316,7 +320,7 @@ export const whereConditionFromPredicateObject = ({ return [`"${field}" ${comparisonOperator} ?`, [operand]]; } - const logicalOperatorKey = operator; + const logicalOperatorKey = operator as keyof typeof logicalOperatorMap; const logicalOperator = logicalOperatorMap[logicalOperatorKey]; @@ -339,10 +343,11 @@ export const whereConditionFromPredicateObject = ({ case 'notContains': statement = [`instr("${field}", ?) ${logicalOperator}`, [operand]]; break; - default: + default: { const _: never = logicalOperatorKey; // Incorrect WHERE clause can result in data loss throw new Error('Cannot map predicate to a valid WHERE clause'); + } } return statement; @@ -350,7 +355,7 @@ export const whereConditionFromPredicateObject = ({ }; export function whereClauseFromPredicate( - predicate: PredicatesGroup + predicate: PredicatesGroup, ): ParameterizedStatement { const result = []; const params = []; @@ -361,13 +366,13 @@ export function whereClauseFromPredicate( return [whereClause, params]; function recurse( - predicate: PredicatesGroup | PredicateObject, - result = [], - params = [] + recursePredicate: PredicatesGroup | PredicateObject, + recurseResult = [], + recurseParams = [], ): void { - if (isPredicateGroup(predicate)) { - const { type: groupType, predicates: groupPredicates } = predicate; - let filterType: string = ''; + if (isPredicateGroup(recursePredicate)) { + const { type: groupType, predicates: groupPredicates } = recursePredicate; + let filterType = ''; let isNegation = false; switch (groupType) { case 'not': @@ -379,25 +384,26 @@ export function whereClauseFromPredicate( case 'or': filterType = 'OR'; break; - default: + default: { const _: never = groupType as never; throw new Error(`Invalid ${groupType}`); + } } const groupResult = []; for (const p of groupPredicates) { - recurse(p, groupResult, params); + recurse(p, groupResult, recurseParams); } - result.push( - `${isNegation ? 'NOT' : ''}(${groupResult.join(` ${filterType} `)})` + recurseResult.push( + `${isNegation ? 'NOT' : ''}(${groupResult.join(` ${filterType} `)})`, ); - } else if (isPredicateObj(predicate)) { + } else if (isPredicateObj(recursePredicate)) { const [condition, conditionParams] = - whereConditionFromPredicateObject(predicate); + whereConditionFromPredicateObject(recursePredicate); - result.push(condition); + recurseResult.push(condition); - params.push(...conditionParams); + recurseParams.push(...conditionParams); } } } @@ -408,11 +414,11 @@ const sortDirectionMap = { }; export function orderByClauseFromSort( - sortPredicate: SortPredicatesGroup = [] + sortPredicate: SortPredicatesGroup = [], ): string { const orderByParts = sortPredicate.map( ({ field, sortDirection }) => - `"${String(field)}" ${sortDirectionMap[sortDirection]}` + `"${String(field)}" ${sortDirectionMap[sortDirection]}`, ); // We always sort by _rowid_ last @@ -423,7 +429,7 @@ export function orderByClauseFromSort( export function limitClauseFromPagination( limit: number, - page: number = 0 + page = 0, ): ParameterizedStatement { const params = [limit]; let clause = 'LIMIT ?'; @@ -441,7 +447,7 @@ export function queryAllStatement( predicate?: PredicatesGroup, sort?: SortPredicatesGroup, limit?: number, - page?: number + page?: number, ): ParameterizedStatement { let statement = `SELECT * FROM "${tableName}"`; const params = []; @@ -466,7 +472,7 @@ export function queryAllStatement( export function queryOneStatement( firstOrLast, - tableName: string + tableName: string, ): ParameterizedStatement { if (firstOrLast === QueryOne.FIRST) { // ORDER BY rowid will no longer work as expected if a customer has @@ -480,15 +486,16 @@ export function queryOneStatement( export function deleteByIdStatement( id: string, - tableName: string + tableName: string, ): ParameterizedStatement { const deleteStatement = `DELETE FROM "${tableName}" WHERE "id"=?`; + return [deleteStatement, [id]]; } export function deleteByPredicateStatement( tableName: string, - predicate?: PredicatesGroup + predicate?: PredicatesGroup, ): ParameterizedStatement { let statement = `DELETE FROM "${tableName}"`; const params = []; @@ -498,5 +505,6 @@ export function deleteByPredicateStatement( statement += ` ${whereClause}`; params.push(...whereParams); } + return [statement, params]; } diff --git a/packages/datastore-storage-adapter/src/common/types.ts b/packages/datastore-storage-adapter/src/common/types.ts index 4d27d6626af..33e1c0f8ea8 100644 --- a/packages/datastore-storage-adapter/src/common/types.ts +++ b/packages/datastore-storage-adapter/src/common/types.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PersistentModel, ModelInstanceMetadata } from '@aws-amplify/datastore'; +import { ModelInstanceMetadata, PersistentModel } from '@aws-amplify/datastore'; export interface CommonSQLiteDatabase { init(): Promise; diff --git a/packages/datastore-storage-adapter/src/index.ts b/packages/datastore-storage-adapter/src/index.ts index 064a0f2b12c..19ba93a509b 100644 --- a/packages/datastore-storage-adapter/src/index.ts +++ b/packages/datastore-storage-adapter/src/index.ts @@ -1,4 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import SQLiteAdapter from './SQLiteAdapter/SQLiteAdapter'; + export { SQLiteAdapter }; diff --git a/packages/datastore-storage-adapter/tsconfig.build.json b/packages/datastore-storage-adapter/tsconfig.build.json new file mode 100644 index 00000000000..48e8892d1b1 --- /dev/null +++ b/packages/datastore-storage-adapter/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__", "ExpoSQLiteAdapter", "SQLiteAdapter"] +} diff --git a/packages/datastore-storage-adapter/tsconfig.json b/packages/datastore-storage-adapter/tsconfig.json index b21bccd02ed..ec41ea5c7f6 100755 --- a/packages/datastore-storage-adapter/tsconfig.json +++ b/packages/datastore-storage-adapter/tsconfig.json @@ -1,4 +1,3 @@ -//WARNING: If you are manually specifying files to compile then the tsconfig.json is completely ignored, you must use command line flags { "compilerOptions": { "target": "es2020", @@ -22,8 +21,12 @@ "allowSyntheticDefaultImports": true, "esModuleInterop": true, "typeRoots": ["./node_modules/@types", "../../node_modules/@types"], - // temporary fix - "types": ["jest", "node", "lodash"] + "types": ["jest", "node", "lodash"], + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["src/**/*"] + "include": ["src/**/*", "ExpoSQLiteAdapter", "SQLiteAdapter"] } diff --git a/packages/datastore-storage-adapter/tsconfig.test.json b/packages/datastore-storage-adapter/tsconfig.test.json new file mode 100644 index 00000000000..3ab6a9946a1 --- /dev/null +++ b/packages/datastore-storage-adapter/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "paths": { + "~/*": ["./*", "../datastore/*"] + } + } +} diff --git a/packages/datastore-storage-adapter/tsconfig.watch.json b/packages/datastore-storage-adapter/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/datastore-storage-adapter/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/datastore-storage-adapter/tslint.json b/packages/datastore-storage-adapter/tslint.json deleted file mode 100644 index 8eafab1d2b4..00000000000 --- a/packages/datastore-storage-adapter/tslint.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/datastore/.eslintrc.js b/packages/datastore/.eslintrc.js new file mode 100644 index 00000000000..9db56f560e0 --- /dev/null +++ b/packages/datastore/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/datastore', + }, + ], + }, +}; diff --git a/packages/datastore/jest.config.js b/packages/datastore/jest.config.js deleted file mode 100644 index f15b76e5c41..00000000000 --- a/packages/datastore/jest.config.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - moduleNameMapper: { - ...require('../../jest.config').moduleNameMapper, - '^dexie$': require.resolve('dexie'), - }, - coverageThreshold: { - global: { - branches: 82, - functions: 94, - lines: 89, - statements: 89, - }, - }, -}; diff --git a/packages/datastore/jest.config.mjs b/packages/datastore/jest.config.mjs new file mode 100644 index 00000000000..e4566a2fad3 --- /dev/null +++ b/packages/datastore/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + moduleNameMapper: { + '^dexie$': requireResolve('dexie'), + }, + coverageThreshold: { + global: { + branches: 82, + functions: 94, + lines: 89, + statements: 89, + }, + }, +}); + +export default jestConfig; diff --git a/packages/datastore/package.json b/packages/datastore/package.json index 94c9344f47f..26f5f22df4c 100644 --- a/packages/datastore/package.json +++ b/packages/datastore/package.json @@ -14,19 +14,20 @@ "./dist/esm/datastore/datastore.mjs" ], "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\" && npm run ts-coverage", - "lint": "tslint '{__tests__,src}/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 92.05" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 92.05" }, "repository": { "type": "git", @@ -63,8 +64,7 @@ "dexie-export-import": "1.0.3", "fake-indexeddb": "^4.0.2", "graphql": "15.8.0", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/datastore/src/authModeStrategies/defaultAuthStrategy.ts b/packages/datastore/src/authModeStrategies/defaultAuthStrategy.ts index b4317d86bd0..4cdda180f6c 100644 --- a/packages/datastore/src/authModeStrategies/defaultAuthStrategy.ts +++ b/packages/datastore/src/authModeStrategies/defaultAuthStrategy.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AuthModeStrategy } from '../types'; +import { AuthModeStrategy } from '~/src/types'; // Default behavior is to use the primary auth mode for an API, // so we are returning an empty array so that DataStore will default diff --git a/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts b/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts index d5b1c1ebef0..c5597f492a6 100644 --- a/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts +++ b/packages/datastore/src/authModeStrategies/multiAuthStrategy.ts @@ -2,16 +2,16 @@ // SPDX-License-Identifier: Apache-2.0 import { fetchAuthSession } from '@aws-amplify/core'; import { + AmplifyContext, AuthModeStrategy, + ModelAttributeAuthAllow, ModelAttributeAuthProperty, ModelAttributeAuthProvider, - ModelAttributeAuthAllow, - AmplifyContext, -} from '../types'; +} from '~/src/types'; import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils'; function getProviderFromRule( - rule: ModelAttributeAuthProperty + rule: ModelAttributeAuthProperty, ): ModelAttributeAuthProvider { // private with no provider means userPools if (rule.allow === 'private' && !rule.provider) { @@ -21,6 +21,7 @@ function getProviderFromRule( if (rule.allow === 'public' && !rule.provider) { return ModelAttributeAuthProvider.API_KEY; } + return rule.provider!; } @@ -48,10 +49,11 @@ function sortAuthRulesWithPriority(rules: ModelAttributeAuthProperty[]) { providerSortPriority.indexOf(getProviderFromRule(b)) ); } + return ( allowSortPriority.indexOf(a.allow) - allowSortPriority.indexOf(b.allow) ); - } + }, ); } @@ -136,9 +138,9 @@ function getAuthRules({ * @returns A sorted array of auth modes to attempt. */ export const multiAuthStrategy: ( - amplifyContext: AmplifyContext + amplifyContext: AmplifyContext, ) => AuthModeStrategy = - (amplifyContext: AmplifyContext) => + () => async ({ schema, modelName }) => { let currentUser; try { @@ -158,11 +160,12 @@ export const multiAuthStrategy: ( if (authAttribute?.properties?.rules) { const sortedRules = sortAuthRulesWithPriority( - authAttribute.properties.rules + authAttribute.properties.rules, ); return getAuthRules({ currentUser, rules: sortedRules }); } } + return []; }; diff --git a/packages/datastore/src/datastore/datastore.ts b/packages/datastore/src/datastore/datastore.ts index ebd29d1c73c..21d185b1b90 100644 --- a/packages/datastore/src/datastore/datastore.ts +++ b/packages/datastore/src/datastore/datastore.ts @@ -1,109 +1,109 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { InternalAPI } from '@aws-amplify/api/internals'; -import { Amplify, Hub, Cache, ConsoleLogger } from '@aws-amplify/core'; - +import { Amplify, Cache, ConsoleLogger, Hub } from '@aws-amplify/core'; import { Draft, + Patch, + enablePatches, immerable, produce, setAutoFreeze, - enablePatches, - Patch, } from 'immer'; -import { amplifyUuid, isBrowser } from '@aws-amplify/core/internals/utils'; +import { + BackgroundProcessManager, + amplifyUuid, + isBrowser, +} from '@aws-amplify/core/internals/utils'; import { Observable, SubscriptionLike, filter } from 'rxjs'; -import { defaultAuthStrategy, multiAuthStrategy } from '../authModeStrategies'; import { - isPredicatesAll, + defaultAuthStrategy, + multiAuthStrategy, +} from '~/src/authModeStrategies'; +import { ModelPredicateCreator, ModelSortPredicateCreator, PredicateAll, -} from '../predicates'; -import { Adapter } from '../storage/adapter'; -import { ExclusiveStorage as Storage } from '../storage/storage'; -import { ModelRelationship } from '../storage/relationship'; -import { ControlMessage, SyncEngine } from '../sync'; + isPredicatesAll, +} from '~/src/predicates'; +import { Adapter } from '~/src/storage/adapter'; +import { ExclusiveStorage as Storage } from '~/src/storage/storage'; +import { ModelRelationship } from '~/src/storage/relationship'; +import { ControlMessage, SyncEngine } from '~/src/sync'; import { + AmplifyContext, AuthModeStrategy, + AuthModeStrategyType, ConflictHandler, DataStoreConfig, + DataStoreSnapshot, + ErrorHandler, GraphQLScalarType, + IdentifierFieldOrIdentifierObject, InternalSchema, - isGraphQLScalarType, - isSchemaModelWithAttributes, + ManagedIdentifier, ModelFieldType, ModelInit, ModelInstanceMetadata, ModelPredicate, - ModelField, - SortPredicate, + ModelPredicateExtender, MutableModel, NamespaceResolver, NonModelTypeConstructor, - ProducerPaginationInput, + ObserveQueryOptions, PaginationInput, PersistentModel, PersistentModelConstructor, - ProducerModelPredicate, + PersistentModelMetaData, + ProducerPaginationInput, + RecursiveModelPredicateExtender, Schema, SchemaModel, SchemaNamespace, SchemaNonModel, + SortPredicate, SubscriptionMessage, - DataStoreSnapshot, SyncConflict, SyncError, - TypeConstructorMap, - ErrorHandler, SyncExpression, - AuthModeStrategyType, - isNonModelFieldType, - isModelFieldType, - ObserveQueryOptions, - ManagedIdentifier, - PersistentModelMetaData, - IdentifierFieldOrIdentifierObject, + TypeConstructorMap, + isGraphQLScalarType, isIdentifierObject, - AmplifyContext, - isFieldAssociation, - RecursiveModelPredicateExtender, - ModelPredicateExtender, -} from '../types'; -// tslint:disable:no-duplicate-imports -import type { __modelMeta__ } from '../types'; - + isModelFieldType, + isNonModelFieldType, + isSchemaModelWithAttributes, +} from '~/src/types'; +import type { __modelMeta__ } from '~/src/types'; import { DATASTORE, - errorMessages, - establishRelationAndKeys, - isModelConstructor, - monotonicUlidFactory, + DeferredCallbackResolver, NAMESPACES, STORAGE, SYNC, USER, - isNullOrUndefined, - registerNonModelClass, - sortCompareFunction, - DeferredCallbackResolver, - inMemoryPagination, + errorMessages, + establishRelationAndKeys, extractPrimaryKeyFieldNames, extractPrimaryKeysAndValues, + getTimestampFields, + inMemoryPagination, isIdManaged, isIdOptionallyManaged, + isModelConstructor, + isNullOrUndefined, mergePatches, - getTimestampFields, -} from '../util'; + monotonicUlidFactory, + registerNonModelClass, + sortCompareFunction, +} from '~/src/util'; import { - recursivePredicateFor, - predicateFor, GroupCondition, internals, -} from '../predicates/next'; -import { getIdentifierValue } from '../sync/utils'; -import DataStoreConnectivity from '../sync/datastoreConnectivity'; -import { BackgroundProcessManager } from '@aws-amplify/core/internals/utils'; + predicateFor, + recursivePredicateFor, +} from '~/src/predicates/next'; +import { getIdentifierValue } from '~/src/sync/utils'; +import DataStoreConnectivity from '~/src/sync/datastoreConnectivity'; setAutoFreeze(true); enablePatches(); @@ -112,17 +112,18 @@ const logger = new ConsoleLogger('DataStore'); const ulid = monotonicUlidFactory(Date.now()); -type SettingMetaData = { +interface SettingMetaData { identifier: ManagedIdentifier; readOnlyFields: never; -}; +} declare class Setting { public readonly [__modelMeta__]: SettingMetaData; constructor(init: ModelInit); static copyOf( src: Setting, - mutator: (draft: MutableModel) => void | Setting + mutator: (draft: MutableModel) => void | Setting, ): Setting; + public readonly id: string; public readonly key: string; public readonly value: string; @@ -148,7 +149,7 @@ const modelPatchesMap = new WeakMap< >(); const getModelDefinition = ( - modelConstructor: PersistentModelConstructor + modelConstructor: PersistentModelConstructor, ) => { const namespace = modelNamespaceMap.get(modelConstructor)!; const definition = namespace @@ -165,7 +166,7 @@ const getModelDefinition = ( * @param obj The object to test. */ const isValidModelConstructor = ( - obj: any + obj: any, ): obj is PersistentModelConstructor => { return isModelConstructor(obj) && modelNamespaceMap.has(obj); }; @@ -174,9 +175,10 @@ const namespaceResolver: NamespaceResolver = modelConstructor => { const resolver = modelNamespaceMap.get(modelConstructor); if (!resolver) { throw new Error( - `Namespace Resolver for '${modelConstructor.name}' not found! This is probably a bug in '@amplify-js/datastore'.` + `Namespace Resolver for '${modelConstructor.name}' not found! This is probably a bug in '@amplify-js/datastore'.`, ); } + return resolver; }; @@ -200,12 +202,12 @@ const namespaceResolver: NamespaceResolver = modelConstructor => { * @param modelConstructor The model the predicate will query. */ const buildSeedPredicate = ( - modelConstructor: PersistentModelConstructor + modelConstructor: PersistentModelConstructor, ) => { if (!modelConstructor) throw new Error('Missing modelConstructor'); const modelSchema = getModelDefinition( - modelConstructor as PersistentModelConstructor + modelConstructor as PersistentModelConstructor, ); if (!modelSchema) throw new Error('Missing modelSchema'); @@ -220,6 +222,8 @@ const buildSeedPredicate = ( }; // exporting syncClasses for testing outbox.test.ts +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line import/no-mutable-exports export let syncClasses: TypeConstructorMap; let userClasses: TypeConstructorMap; let dataStoreClasses: TypeConstructorMap; @@ -274,13 +278,14 @@ const attachedModelInstances = new WeakMap(); */ export function attached( result: T, - attachment: ModelAttachment + attachment: ModelAttachment, ): T { if (Array.isArray(result)) { result.map(record => attached(record, attachment)) as T; } else { result && attachedModelInstances.set(result, attachment); } + return result; } @@ -338,7 +343,7 @@ const initSchema = (userSchema: Schema) => { Object.keys(schema.namespaces).forEach(namespace => { const [relations, keys] = establishRelationAndKeys( - schema.namespaces[namespace] + schema.namespaces[namespace], ); schema.namespaces[namespace].relationships = relations; @@ -354,10 +359,10 @@ const initSchema = (userSchema: Schema) => { field => field.association && field.association.connectionType === 'BELONGS_TO' && - (field.type).model !== model.name + (field.type as ModelFieldType).model !== model.name, ) .forEach(field => - connectedModels.push((field.type).model) + connectedModels.push((field.type as ModelFieldType).model), ); modelAssociations.set(model.name, connectedModels); @@ -366,15 +371,16 @@ const initSchema = (userSchema: Schema) => { // (such as predicate builders) don't have to reach back into "DataStore" space // to go looking for it. Object.values(model.fields).forEach(field => { - const relatedModel = userClasses[(field.type).model]; + const relatedModel = userClasses[(field.type as ModelFieldType).model]; if (isModelConstructor(relatedModel)) { Object.defineProperty(field.type, 'modelConstructor', { get: () => { const relatedModelDefinition = getModelDefinition(relatedModel); if (!relatedModelDefinition) throw new Error( - `Could not find model definition for ${relatedModel.name}` + `Could not find model definition for ${relatedModel.name}`, ); + return { builder: relatedModel, schema: relatedModelDefinition, @@ -389,8 +395,8 @@ const initSchema = (userSchema: Schema) => { // index fields into the model definition. // definition.cloudFields = { ...definition.fields }; - const indexes = - schema.namespaces[namespace].relationships![model.name].indexes; + const { indexes } = + schema.namespaces[namespace].relationships![model.name]; const indexFields = new Set(); for (const index of indexes) { @@ -408,7 +414,7 @@ const initSchema = (userSchema: Schema) => { type: 'ID', isArray: false, }, - ]) + ]), ), ...model.fields, }; @@ -424,7 +430,7 @@ const initSchema = (userSchema: Schema) => { count--; if (count === 0) { throw new Error( - 'Models are not topologically sortable. Please verify your schema.' + 'Models are not topologically sortable. Please verify your schema.', ); } @@ -487,7 +493,7 @@ const checkSchemaCodegenVersion = (codegenVersion: string) => { let isValid = false; try { const versionParts = codegenVersion.split('.'); - const [major, minor, patch, patchrevision] = versionParts; + const [major, minor] = versionParts; isValid = Number(major) === majorVersion && Number(minor) >= minorVersion; } catch (err) { console.log(`Error parsing codegen version: ${codegenVersion}\n${err}`); @@ -505,7 +511,7 @@ const checkSchemaCodegenVersion = (codegenVersion: string) => { }; const createTypeClasses: ( - namespace: SchemaNamespace + namespace: SchemaNamespace, ) => TypeConstructorMap = namespace => { const classes: TypeConstructorMap = {}; @@ -520,7 +526,7 @@ const createTypeClasses: ( ([typeName, typeDefinition]) => { const clazz = createNonModelClass(typeDefinition); classes[typeName] = clazz; - } + }, ); return classes; @@ -545,12 +551,12 @@ export declare type ModelInstanceCreator = typeof modelInstanceCreator; const instancesMetadata = new WeakSet>(); function modelInstanceCreator( - modelConstructor: PersistentModelConstructor, - init: Partial + ModelConstructor: PersistentModelConstructor, + init: Partial, ): T { instancesMetadata.add(init); - return new modelConstructor(>>init); + return new ModelConstructor(init as ModelInit>); } const validateModelFields = @@ -596,6 +602,7 @@ const validateModelFields = if (typeof v === 'string') { try { JSON.parse(v); + return; } catch (error) { throw new Error(`Field ${name} is an invalid JSON object. ${v}`); @@ -611,27 +618,27 @@ const validateModelFields = if (!Array.isArray(v) && !isArrayNullable) { throw new Error( - `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}` + `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}`, ); } if ( !isNullOrUndefined(v) && - (<[]>v).some(e => - isNullOrUndefined(e) ? isRequired : typeof e !== jsType + (v as []).some(e => + isNullOrUndefined(e) ? isRequired : typeof e !== jsType, ) ) { - const elemTypes = (<[]>v) + const elemTypes = (v as []) .map(e => (e === null ? 'null' : typeof e)) .join(','); throw new Error( - `All elements in the ${name} array should be of type ${errorTypeText}, [${elemTypes}] received. ${v}` + `All elements in the ${name} array should be of type ${errorTypeText}, [${elemTypes}] received. ${v}`, ); } if (validateScalar && !isNullOrUndefined(v)) { - const validationStatus = (<[]>v).map(e => { + const validationStatus = (v as []).map(e => { if (!isNullOrUndefined(e)) { return validateScalar(e); } else if (isNullOrUndefined(e) && !isRequired) { @@ -643,15 +650,15 @@ const validateModelFields = if (!validationStatus.every(s => s)) { throw new Error( - `All elements in the ${name} array should be of type ${type}, validation failed for one or more elements. ${v}` + `All elements in the ${name} array should be of type ${type}, validation failed for one or more elements. ${v}`, ); } } } else if (!isRequired && v === undefined) { - return; + // no-op } else if (typeof v !== jsType && v !== null) { throw new Error( - `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}` + `Field ${name} should be of type ${jsType}, ${typeof v} received. ${v}`, ); } else if ( !isNullOrUndefined(v) && @@ -659,7 +666,7 @@ const validateModelFields = !validateScalar(v as never) // TODO: why never, TS ... why ... ) { throw new Error( - `Field ${name} should be of type ${type}, validation failed. ${v}` + `Field ${name} should be of type ${type}, validation failed. ${v}`, ); } } else if (isNonModelFieldType(type)) { @@ -676,7 +683,7 @@ const validateModelFields = } if (!Array.isArray(v)) { throw new Error( - `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}` + `Field ${name} should be of type [${errorTypeText}], ${typeof v} received. ${v}`, ); } @@ -688,7 +695,7 @@ const validateModelFields = throw new Error( `All elements in the ${name} array should be of type ${ type.nonModel - }, [${typeof item}] received. ${item}` + }, [${typeof item}] received. ${item}`, ); } @@ -703,7 +710,7 @@ const validateModelFields = throw new Error( `Field ${name} should be of type ${ type.nonModel - }, ${typeof v} recieved. ${v}` + }, ${typeof v} recieved. ${v}`, ); } @@ -719,7 +726,7 @@ const validateModelFields = const castInstanceType = ( modelDefinition: SchemaModel | SchemaNonModel, k: string, - v: any + v: any, ) => { const { isArray, type } = modelDefinition.fields[k] || {}; // attempt to parse stringified JSON @@ -763,14 +770,14 @@ const initPatches = new WeakMap(); const initializeInstance = ( init: ModelInit, modelDefinition: SchemaModel | SchemaNonModel, - draft: Draft + draft: Draft, ) => { const modelValidator = validateModelFields(modelDefinition); Object.entries(init).forEach(([k, v]) => { const parsedValue = castInstanceType(modelDefinition, k, v); modelValidator(k, parsedValue); - (draft)[k] = parsedValue; + (draft as any)[k] = parsedValue; }); }; @@ -795,17 +802,17 @@ const initializeInstance = ( */ const normalize = ( modelDefinition: SchemaModel | SchemaNonModel, - draft: Draft + draft: Draft, ) => { for (const k of Object.keys(modelDefinition.fields)) { - if (draft[k] === undefined) (draft)[k] = null; + if (draft[k] === undefined) (draft as any)[k] = null; } }; const createModelClass = ( - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ) => { - const clazz = >(class Model { + const clazz = class Model { constructor(init: ModelInit) { // we create a base instance first so we can distinguish which fields were explicitly // set by customer code versus those set by normalization. only those fields @@ -821,10 +828,12 @@ const createModelClass = ( const modelInstanceMetadata: ModelInstanceMetadata = isInternallyInitialized - ? (init) - : {}; + ? (init as unknown as ModelInstanceMetadata) + : ({} as ModelInstanceMetadata); - type ModelWithIDIdentifier = { id: string }; + interface ModelWithIDIdentifier { + id: string; + } const { id: _id } = modelInstanceMetadata as unknown as ModelWithIDIdentifier; @@ -838,10 +847,10 @@ const createModelClass = ( ? amplifyUuid() : ulid(); - ((draft)).id = id; + (draft as unknown as ModelWithIDIdentifier).id = id; } else if (isIdOptionallyManaged(modelDefinition)) { // only auto-populate if the id was not provided - ((draft)).id = + (draft as unknown as ModelWithIDIdentifier).id = draft.id || amplifyUuid(); } @@ -857,7 +866,7 @@ const createModelClass = ( draft._deleted = _deleted; } }, - p => (patches = p) + p => (patches = p), ); // now that we have a list of patches that encapsulate the explicit, customer-provided @@ -867,8 +876,9 @@ const createModelClass = ( // "cloud managed" fields, like createdAt and updatedAt.) const normalized = produce( baseInstance, - (draft: Draft) => - normalize(modelDefinition, draft) + (draft: Draft) => { + normalize(modelDefinition, draft); + }, ); initPatches.set(normalized, patches); @@ -888,7 +898,7 @@ const createModelClass = ( const model = produce( source, draft => { - fn(>draft); + fn(draft as MutableModel); const keyNames = extractPrimaryKeyFieldNames(modelDefinition); // Keys are immutable @@ -896,10 +906,10 @@ const createModelClass = ( if (draft[key] !== source[key]) { logger.warn( `copyOf() does not update PK fields. The '${key}' update is being ignored.`, - { source } + { source }, ); } - (draft as Object)[key] = source[key]; + (draft as any)[key] = source[key]; }); const modelValidator = validateModelFields(modelDefinition); @@ -911,7 +921,7 @@ const createModelClass = ( normalize(modelDefinition, draft); }, - p => (patches = p) + p => (patches = p), ); const hasExistingPatches = modelPatchesMap.has(source); @@ -923,7 +933,7 @@ const createModelClass = ( const mergedPatches = mergePatches( existingSource, existingPatches, - patches + patches, ); modelPatchesMap.set(model, [mergedPatches, existingSource]); checkReadOnlyPropertyOnUpdate(mergedPatches, modelDefinition); @@ -961,7 +971,7 @@ const createModelClass = ( return attached(instance, ModelAttachment.DataStore); } - }); + } as unknown as PersistentModelConstructor; clazz[immerable] = true; @@ -976,7 +986,7 @@ const createModelClass = ( pkField: extractPrimaryKeyFieldNames(modelDefinition), }); for (const relationship of allModelRelationships) { - const field = relationship.field; + const { field } = relationship; Object.defineProperty(clazz.prototype, modelDefinition.fields[field].name, { set(model: T | undefined | null) { @@ -988,7 +998,7 @@ const createModelClass = ( // Avoid validation error when processing AppSync response with nested // selection set. Nested entitites lack version field and can not be validated // TODO: explore a more reliable method to solve this - if (model.hasOwnProperty('_version')) { + if (Object.prototype.hasOwnProperty.call(model, '_version')) { const modelConstructor = Object.getPrototypeOf(model || {}) .constructor as PersistentModelConstructor; @@ -1034,7 +1044,7 @@ const createModelClass = ( // if the memos already has a result for this field, we'll use it. // there is no "cache" invalidation of any kind; memos are permanent to // keep an immutable perception of the instance. - if (!instanceMemos.hasOwnProperty(field)) { + if (!Object.prototype.hasOwnProperty.call(instanceMemos, field)) { // before we populate the memo, we need to know where to look for relatives. // today, this only supports DataStore. Models aren't managed elsewhere in Amplify. if (getAttachment(this) === ModelAttachment.DataStore) { @@ -1046,13 +1056,15 @@ const createModelClass = ( relationship.remoteModelConstructor as PersistentModelConstructor, base => base.and(q => { - return relationship.remoteJoinFields.map((field, index) => { - // TODO: anything we can use instead of `any` here? - return (q[field] as T[typeof field]).eq( - this[relationship.localJoinFields[index]] - ); - }); - }) + return relationship.remoteJoinFields.map( + (remoteJointField, index) => { + // TODO: anything we can use instead of `any` here? + return ( + q[remoteJointField] as T[typeof remoteJointField] + ).eq(this[relationship.localJoinFields[index]]); + }, + ); + }), ); // results in hand, how we return them to the caller depends on the relationship type. @@ -1108,9 +1120,9 @@ export class AsyncItem extends Promise {} * This collection can be async-iterated or turned directly into an array using `toArray()`. */ export class AsyncCollection implements AsyncIterable { - private values: Array | Promise>; + private values: any[] | Promise; - constructor(values: Array | Promise>) { + constructor(values: any[] | Promise) { this.values = values; } @@ -1128,6 +1140,7 @@ export class AsyncCollection implements AsyncIterable { [Symbol.asyncIterator](): AsyncIterator { let values; let index = 0; + return { next: async () => { if (!values) values = await this.values; @@ -1137,8 +1150,10 @@ export class AsyncCollection implements AsyncIterable { done: false, }; index++; + return result; } + return { value: null, done: true, @@ -1168,13 +1183,14 @@ export class AsyncCollection implements AsyncIterable { break; } } + return output; } } const checkReadOnlyPropertyOnCreate = ( draft: T, - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ) => { const modelKeys = Object.keys(draft); const { fields } = modelDefinition; @@ -1188,7 +1204,7 @@ const checkReadOnlyPropertyOnCreate = ( const checkReadOnlyPropertyOnUpdate = ( patches: Patch[], - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ) => { const patchArray = patches.map(p => [p.path[0], p.value]); const { fields } = modelDefinition; @@ -1203,20 +1219,20 @@ const checkReadOnlyPropertyOnUpdate = ( }; const createNonModelClass = ( - typeDefinition: SchemaNonModel + typeDefinition: SchemaNonModel, ) => { - const clazz = >(class Model { + const clazz = class Model { constructor(init: ModelInit) { const instance = produce( this, (draft: Draft) => { initializeInstance(init, typeDefinition, draft); - } + }, ); return instance; } - }); + } as unknown as NonModelTypeConstructor; clazz[immerable] = true; @@ -1234,6 +1250,7 @@ function isQueryOne(obj: any): obj is string { function defaultConflictHandler(conflictData: SyncConflict): PersistentModel { const { localModel, modelConstructor, remoteModel } = conflictData; const { _version } = remoteModel; + return modelInstanceCreator(modelConstructor, { ...localModel, _version }); } @@ -1243,7 +1260,7 @@ function defaultErrorHandler(error: SyncError): void { function getModelConstructorByModelName( namespaceName: NAMESPACES, - modelName: string + modelName: string, ): PersistentModelConstructor { let result: PersistentModelConstructor | NonModelTypeConstructor; @@ -1288,20 +1305,20 @@ function getModelConstructorByModelName( */ async function checkSchemaVersion( storage: Storage, - version: string + version: string, ): Promise { - const Setting = + const datastoreClassesSetting = dataStoreClasses.Setting as PersistentModelConstructor; const modelDefinition = schema.namespaces[DATASTORE].models.Setting; await storage.runExclusive(async s => { const [schemaVersionSetting] = await s.query( - Setting, + datastoreClassesSetting, ModelPredicateCreator.createFromAST(modelDefinition, { and: { key: { eq: SETTING_SCHEMA_VERSION } }, }), - { page: 0, limit: 1 } + { page: 0, limit: 1 }, ); if ( @@ -1315,10 +1332,10 @@ async function checkSchemaVersion( } } else { await s.save( - modelInstanceCreator(Setting, { + modelInstanceCreator(datastoreClassesSetting, { key: SETTING_SCHEMA_VERSION, value: JSON.stringify(version), - }) + }), ); } }); @@ -1393,8 +1410,8 @@ class DataStore { private errorHandler!: (error: SyncError) => void; private fullSyncInterval!: number; private initialized?: Promise; - private initReject!: Function; - private initResolve!: Function; + private initReject!: (...args: any[]) => void; + private initResolve!: (...args: any[]) => void; private maxRecordsToSync!: number; private storage?: Storage; private sync?: SyncEngine; @@ -1402,12 +1419,14 @@ class DataStore { private syncExpressions!: SyncExpression[]; private syncPredicates: WeakMap | null> = new WeakMap>(); + private sessionId?: string; private storageAdapter!: Adapter; // object that gets passed to descendent classes. Allows us to pass these down by reference private amplifyContext: AmplifyContext = { InternalAPI: this.InternalAPI, }; + private connectivityMonitor?: DataStoreConnectivity; /** @@ -1477,7 +1496,7 @@ class DataStore { `This can only be done while DataStore is "Started" or "Stopped". To remedy:`, 'Ensure all calls to `stop()` and `clear()` have completed first.', 'If this is not possible, retry the operation until it succeeds.', - ].join('\n') + ].join('\n'), ); } else { throw err; @@ -1500,12 +1519,13 @@ class DataStore { this.state = DataStoreState.Starting; if (this.initialized === undefined) { logger.debug('Starting DataStore'); - this.initialized = new Promise((res, rej) => { - this.initResolve = res; - this.initReject = rej; + this.initialized = new Promise((resolve, reject) => { + this.initResolve = resolve; + this.initReject = reject; }); } else { await this.initialized; + return; } @@ -1515,7 +1535,7 @@ class DataStore { getModelConstructorByModelName, modelInstanceCreator, this.storageAdapter, - this.sessionId + this.sessionId, ); await this.storage.init(); @@ -1527,7 +1547,7 @@ class DataStore { if (aws_appsync_graphqlEndpoint) { logger.debug( 'GraphQL endpoint available', - aws_appsync_graphqlEndpoint + aws_appsync_graphqlEndpoint, ); this.syncPredicates = await this.processSyncExpressions(); @@ -1544,7 +1564,7 @@ class DataStore { this.amplifyConfig, this.authModeStrategy, this.amplifyContext, - this.connectivityMonitor + this.connectivityMonitor, ); const fullSyncIntervalInMilliseconds = @@ -1577,7 +1597,7 @@ class DataStore { "Data won't be synchronized. No GraphQL endpoint configured. Did you forget `Amplify.configure(awsconfig)`?", { config: this.amplifyConfig, - } + }, ); this.initResolve(); @@ -1595,7 +1615,7 @@ class DataStore { identifier: IdentifierFieldOrIdentifierObject< T, PersistentModelMetaData - > + >, ): Promise; ( modelConstructor: PersistentModelConstructor, @@ -1603,7 +1623,7 @@ class DataStore { | RecursiveModelPredicateExtender | typeof PredicateAll | null, - paginationProducer?: ProducerPaginationInput + paginationProducer?: ProducerPaginationInput, ): Promise; } = async ( modelConstructor: PersistentModelConstructor, @@ -1612,7 +1632,7 @@ class DataStore { | RecursiveModelPredicateExtender | typeof PredicateAll | null, - paginationProducer?: ProducerPaginationInput + paginationProducer?: ProducerPaginationInput, ): Promise => { return this.runningProcesses .add(async () => { @@ -1624,7 +1644,7 @@ class DataStore { throw new Error('No storage to query'); } - //#region Input validation + // #region Input validation if (!isValidModelConstructor(modelConstructor)) { const msg = 'Constructor is not for a valid model'; @@ -1645,7 +1665,7 @@ class DataStore { const pagination = this.processPagination( modelDefinition, - paginationProducer + paginationProducer, ); const keyFields = extractPrimaryKeyFieldNames(modelDefinition); @@ -1660,25 +1680,25 @@ class DataStore { const predicate = ModelPredicateCreator.createFromFlatEqualities( modelDefinition, - { [keyFields[0]]: identifierOrCriteria } + { [keyFields[0]]: identifierOrCriteria }, ); result = await this.storage.query( modelConstructor, predicate, - pagination + pagination, ); } else { // Object is being queried using object literal syntax - if (isIdentifierObject(identifierOrCriteria, modelDefinition)) { + if (isIdentifierObject(identifierOrCriteria as T, modelDefinition)) { const predicate = ModelPredicateCreator.createForPk( modelDefinition, - identifierOrCriteria + identifierOrCriteria as T, ); result = await this.storage.query( modelConstructor, predicate, - pagination + pagination, ); } else if ( !identifierOrCriteria || @@ -1687,7 +1707,7 @@ class DataStore { result = await this.storage?.query( modelConstructor, undefined, - pagination + pagination, ); } else { const seedPredicate = recursivePredicateFor({ @@ -1697,15 +1717,15 @@ class DataStore { }); const predicate = internals( (identifierOrCriteria as RecursiveModelPredicateExtender)( - seedPredicate - ) + seedPredicate, + ), ); result = (await predicate.fetch(this.storage)) as T[]; result = inMemoryPagination(result, pagination); } } - //#endregion + // #endregion const returnOne = isQueryOne(identifierOrCriteria) || @@ -1713,7 +1733,7 @@ class DataStore { return attached( returnOne ? result[0] : result, - ModelAttachment.DataStore + ModelAttachment.DataStore, ); }, 'datastore query') .catch(this.handleAddProcError('DataStore.query()')); @@ -1721,7 +1741,7 @@ class DataStore { save = async ( model: T, - condition?: ModelPredicateExtender + condition?: ModelPredicateExtender, ): Promise => { return this.runningProcesses .add(async () => { @@ -1752,7 +1772,9 @@ class DataStore { | undefined = updatedPatchesTuple || initPatchesTuple; const modelConstructor: PersistentModelConstructor | undefined = - model ? >model.constructor : undefined; + model + ? (model.constructor as PersistentModelConstructor) + : undefined; if (!isValidModelConstructor(modelConstructor)) { const msg = 'Object is not an instance of a valid model'; @@ -1776,7 +1798,7 @@ class DataStore { // no enforcement for HAS_MANY on save, because the ~related~ entities // hold the FK in that case. const nonHasManyRelationships = ModelRelationship.allFrom( - modelMeta + modelMeta, ).filter(r => r.type === 'BELONGS_TO'); for (const relationship of nonHasManyRelationships) { const queryObject = relationship.createRemoteQueryObject(model); @@ -1785,8 +1807,8 @@ class DataStore { relationship.remoteModelConstructor, ModelPredicateCreator.createFromFlatEqualities( relationship.remoteDefinition!, - queryObject - ) + queryObject, + ), ); if (related.length === 0) { throw new Error( @@ -1797,7 +1819,7 @@ class DataStore { `but the instance assigned to the "${relationship.field}" property`, `does not exist in the local database. If you're trying to create the related`, `"${relationship.remoteDefinition?.name}", you must save it independently first.`, - ].join(' ') + ].join(' '), ); } } @@ -1806,20 +1828,16 @@ class DataStore { const producedCondition = condition ? internals( - condition(predicateFor(modelMeta)) + condition(predicateFor(modelMeta)), ).toStoragePredicate() : undefined; const [savedModel] = await this.storage.runExclusive(async s => { - const saved = await s.save( - model, - producedCondition, - undefined, - patchesTuple - ); + await s.save(model, producedCondition, undefined, patchesTuple); + return s.query( modelConstructor, - ModelPredicateCreator.createForPk(modelDefinition, model) + ModelPredicateCreator.createForPk(modelDefinition, model), ); }); @@ -1866,22 +1884,22 @@ class DataStore { identifier: IdentifierFieldOrIdentifierObject< T, PersistentModelMetaData - > + >, ): Promise; ( modelConstructor: PersistentModelConstructor, - condition: ModelPredicateExtender | typeof PredicateAll + condition: ModelPredicateExtender | typeof PredicateAll, ): Promise; ( model: T, - condition?: ModelPredicateExtender + condition?: ModelPredicateExtender, ): Promise; } = async ( modelOrConstructor: T | PersistentModelConstructor, identifierOrCriteria?: | IdentifierFieldOrIdentifierObject> | ModelPredicateExtender - | typeof PredicateAll + | typeof PredicateAll, ): Promise => { return this.runningProcesses .add(async () => { @@ -1915,7 +1933,7 @@ class DataStore { if (!modelDefinition) { throw new Error( - 'Could not find model definition for modelConstructor.' + 'Could not find model definition for modelConstructor.', ); } @@ -1931,13 +1949,13 @@ class DataStore { condition = ModelPredicateCreator.createFromFlatEqualities( modelDefinition, - { [keyFields[0]]: identifierOrCriteria } + { [keyFields[0]]: identifierOrCriteria }, ); } else { if (isIdentifierObject(identifierOrCriteria, modelDefinition)) { condition = ModelPredicateCreator.createForPk( modelDefinition, - identifierOrCriteria + identifierOrCriteria as T, ); } else { condition = internals( @@ -1946,8 +1964,8 @@ class DataStore { builder: modelConstructor as PersistentModelConstructor, schema: modelDefinition, pkField: extractPrimaryKeyFieldNames(modelDefinition), - }) - ) + }), + ), ).toStoragePredicate(); } @@ -1965,7 +1983,7 @@ class DataStore { const [deleted] = await this.storage.delete( modelConstructor, - condition + condition, ); return attached(deleted, ModelAttachment.DataStore); @@ -1985,13 +2003,13 @@ class DataStore { if (!modelDefinition) { throw new Error( - 'Could not find model definition for modelConstructor.' + 'Could not find model definition for modelConstructor.', ); } const pkPredicate = ModelPredicateCreator.createForPk( modelDefinition, - model + model, ); if (identifierOrCriteria) { @@ -2008,8 +2026,8 @@ class DataStore { builder: modelConstructor as PersistentModelConstructor, schema: modelDefinition, pkField: extractPrimaryKeyFieldNames(modelDefinition), - }) - ) + }), + ), ).toStoragePredicate(); } else { condition = pkPredicate; @@ -2028,12 +2046,12 @@ class DataStore { ( modelConstructor: PersistentModelConstructor, - identifier: string + identifier: string, ): Observable>; ( modelConstructor: PersistentModelConstructor, - criteria?: RecursiveModelPredicateExtender | typeof PredicateAll + criteria?: RecursiveModelPredicateExtender | typeof PredicateAll, ): Observable>; (model: T): Observable>; @@ -2042,7 +2060,7 @@ class DataStore { identifierOrCriteria?: | string | RecursiveModelPredicateExtender - | typeof PredicateAll + | typeof PredicateAll, ): Observable> => { let executivePredicate: GroupCondition; @@ -2052,11 +2070,11 @@ class DataStore { : undefined; if (modelOrConstructor && modelConstructor === undefined) { - const model = modelOrConstructor; - const modelConstructor = - model && (Object.getPrototypeOf(model)).constructor; + const model = modelOrConstructor as T; + const theModelConstructor = + model && (Object.getPrototypeOf(model) as any).constructor; - if (isValidModelConstructor(modelConstructor)) { + if (isValidModelConstructor(theModelConstructor)) { if (identifierOrCriteria) { logger.warn('idOrCriteria is ignored when using a model instance', { model, @@ -2064,7 +2082,7 @@ class DataStore { }); } - return this.observe(modelConstructor, model.id); + return this.observe(theModelConstructor, model.id); } else { const msg = 'The model is not an instance of a PersistentModelConstructor'; @@ -2080,7 +2098,7 @@ class DataStore { modelConstructor && isIdentifierObject( identifierOrCriteria, - getModelDefinition(modelConstructor!)! + getModelDefinition(modelConstructor!)!, ) ) { const msg = errorMessages.observeWithObjectLiteral; @@ -2105,13 +2123,13 @@ class DataStore { if (modelConstructor && typeof identifierOrCriteria === 'string') { const buildIdPredicate = seed => seed.id.eq(identifierOrCriteria); executivePredicate = internals( - buildIdPredicate(buildSeedPredicate(modelConstructor)) + buildIdPredicate(buildSeedPredicate(modelConstructor)), ); } else if (modelConstructor && typeof identifierOrCriteria === 'function') { executivePredicate = internals( (identifierOrCriteria as RecursiveModelPredicateExtender)( - buildSeedPredicate(modelConstructor) - ) + buildSeedPredicate(modelConstructor), + ), ); } @@ -2141,15 +2159,15 @@ class DataStore { if (item.opType !== 'DELETE') { const modelDefinition = getModelDefinition(item.model); const keyFields = extractPrimaryKeyFieldNames( - modelDefinition! + modelDefinition!, ); const primaryKeysAndValues = extractPrimaryKeysAndValues( item.element, - keyFields + keyFields, ); const freshElement = await this.query( item.model, - primaryKeysAndValues + primaryKeysAndValues, ); message = { ...message, @@ -2164,8 +2182,12 @@ class DataStore { observer.next(message as SubscriptionMessage); } }, 'datastore observe message handler'), - error: err => observer.error(err), - complete: () => observer.complete(), + error: err => { + observer.error(err); + }, + complete: () => { + observer.complete(); + }, }); }, 'datastore observe observable initialization') .catch(this.handleAddProcError('DataStore.observe()')) @@ -2184,16 +2206,14 @@ class DataStore { }); }; - observeQuery: { - ( - modelConstructor: PersistentModelConstructor, - criteria?: RecursiveModelPredicateExtender | typeof PredicateAll, - paginationProducer?: ObserveQueryOptions - ): Observable>; - } = ( + observeQuery: ( + modelConstructor: PersistentModelConstructor, + criteria?: RecursiveModelPredicateExtender | typeof PredicateAll, + paginationProducer?: ObserveQueryOptions, + ) => Observable> = ( model: PersistentModelConstructor, criteria?: RecursiveModelPredicateExtender | typeof PredicateAll, - options?: ObserveQueryOptions + options?: ObserveQueryOptions, ): Observable> => { return new Observable>(observer => { const items = new Map(); @@ -2237,8 +2257,8 @@ class DataStore { if (model && typeof criteria === 'function') { executivePredicate = internals( (criteria as RecursiveModelPredicateExtender)( - buildSeedPredicate(model) - ) + buildSeedPredicate(model), + ), ); } else if (isPredicatesAll(criteria)) { executivePredicate = undefined; @@ -2259,13 +2279,14 @@ class DataStore { // to have visibility into items that move from in-set to out-of-set. // We need to explicitly remove those items from the existing snapshot. handle = this.observe(model).subscribe( - ({ element, model, opType }) => + ({ element, model: subscribedModel, opType }) => this.runningProcesses.isOpen && this.runningProcesses.add(async () => { - const itemModelDefinition = getModelDefinition(model)!; + const itemModelDefinition = + getModelDefinition(subscribedModel)!; const idOrPk = getIdentifierValue( itemModelDefinition, - element + element, ); if ( executivePredicate && @@ -2297,7 +2318,7 @@ class DataStore { } const isSynced = - this.sync?.getModelSyncedStatus(model) ?? false; + this.sync?.getModelSyncedStatus(subscribedModel) ?? false; const limit = itemsChanged.size - deletedItemIds.length >= @@ -2309,7 +2330,7 @@ class DataStore { // kicks off every subsequent race as results sync down limitTimerRace.start(); - }, 'handle observeQuery observed event') + }, 'handle observeQuery observed event'), ); // returns a set of initial/locally-available results @@ -2386,11 +2407,14 @@ class DataStore { * @param itemsToSort A array of model type. */ const sortItems = (itemsToSort: T[]): void => { - const modelDefinition = getModelDefinition(model); - const pagination = this.processPagination(modelDefinition!, options); + const modelDefinitionToSort = getModelDefinition(model); + const pagination = this.processPagination( + modelDefinitionToSort!, + options, + ); const sortPredicates = ModelSortPredicateCreator.getPredicates( - pagination!.sort! + pagination!.sort!, ); if (sortPredicates.length) { @@ -2433,8 +2457,6 @@ class DataStore { const { DataStore: configDataStore, authModeStrategyType: configAuthModeStrategyType, - conflictHandler: configConflictHandler, - errorHandler: configErrorHandler, maxRecordsToSync: configMaxRecordsToSync, syncPageSize: configSyncPageSize, fullSyncInterval: configFullSyncInterval, @@ -2541,7 +2563,7 @@ class DataStore { getModelConstructorByModelName, modelInstanceCreator, this.storageAdapter, - this.sessionId + this.sessionId, ); await this.storage.init(); } @@ -2599,7 +2621,7 @@ class DataStore { */ private processPagination( modelDefinition: SchemaModel, - paginationProducer?: ProducerPaginationInput + paginationProducer?: ProducerPaginationInput, ): PaginationInput | undefined { let sortPredicate: SortPredicate | undefined; const { limit, page, sort } = paginationProducer || {}; @@ -2635,7 +2657,7 @@ class DataStore { if (sort) { sortPredicate = ModelSortPredicateCreator.createFromExisting( modelDefinition, - sort + sort, ); } @@ -2660,7 +2682,7 @@ class DataStore { const syncPredicates = await Promise.all( this.syncExpressions.map( async ( - syncExpression: SyncExpression + syncExpression: SyncExpression, ): Promise<[SchemaModel, ModelPredicate | null]> => { const { modelConstructor, conditionProducer } = await syncExpression; const modelDefinition = getModelDefinition(modelConstructor)!; @@ -2678,23 +2700,24 @@ class DataStore { builder: modelConstructor, schema: modelDefinition, pkField: extractPrimaryKeyFieldNames(modelDefinition), - }) - ) + }), + ), ).toStoragePredicate(); return [modelDefinition, predicate]; - } - ) + }, + ), ); return this.weakMapFromEntries(syncPredicates); } private async unwrapPromise( - conditionProducer + conditionProducer, ): Promise> { try { const condition = await conditionProducer(); + return condition || conditionProducer; } catch (error) { if (error instanceof TypeError) { @@ -2705,15 +2728,16 @@ class DataStore { } private weakMapFromEntries( - entries: [SchemaModel, ModelPredicate | null][] + entries: [SchemaModel, ModelPredicate | null][], ): WeakMap> { return entries.reduce((map, [modelDefinition, predicate]) => { if (map.has(modelDefinition)) { const { name } = modelDefinition; logger.warn( `You can only utilize one Sync Expression per model. - Subsequent sync expressions for the ${name} model will be ignored.` + Subsequent sync expressions for the ${name} model will be ignored.`, ); + return map; } diff --git a/packages/datastore/src/index.ts b/packages/datastore/src/index.ts index 81f67de2dbc..7f6a8ce807e 100644 --- a/packages/datastore/src/index.ts +++ b/packages/datastore/src/index.ts @@ -1,10 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { + USER, + isModelConstructor, + isNonModelConstructor, + traverseModel, + validatePredicate, +} from './util'; + export { DataStore, DataStoreClass, initSchema, ModelInstanceCreator, + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line import/export AsyncCollection, AsyncItem, } from './datastore/datastore'; @@ -16,14 +26,6 @@ export { } from './predicates'; export { Adapter as StorageAdapter } from './storage/adapter'; -import { - traverseModel, - validatePredicate, - USER, - isNonModelConstructor, - isModelConstructor, -} from './util'; - export { NAMESPACES } from './util'; export const utils = { @@ -34,4 +36,6 @@ export const utils = { isModelConstructor, }; +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line import/export export * from './types'; diff --git a/packages/datastore/src/predicates/index.ts b/packages/datastore/src/predicates/index.ts index 19dddd5d546..dfed17a9f81 100644 --- a/packages/datastore/src/predicates/index.ts +++ b/packages/datastore/src/predicates/index.ts @@ -6,15 +6,18 @@ import { PredicatesGroup, ProducerModelPredicate, SchemaModel, -} from '../types'; -import { extractPrimaryKeyFieldNames, extractPrimaryKeyValues } from '../util'; +} from '~/src/types'; +import { + extractPrimaryKeyFieldNames, + extractPrimaryKeyValues, +} from '~/src/util'; export { ModelSortPredicateCreator } from './sort'; const predicatesAllSet = new WeakSet>(); export function isPredicatesAll( - predicate: any + predicate: any, ): predicate is typeof PredicateAll { return predicatesAllSet.has(predicate); } @@ -39,6 +42,7 @@ const groupKeys = new Set(['and', 'or', 'not']); */ const isGroup = o => { const keys = [...Object.keys(o)]; + return keys.length === 1 && groupKeys.has(keys[0]); }; @@ -77,6 +81,7 @@ export const comparisonKeys = new Set([ */ const isComparison = o => { const keys = [...Object.keys(o)]; + return !Array.isArray(o) && keys.length === 1 && comparisonKeys.has(keys[0]); }; @@ -98,11 +103,13 @@ export const PredicateAll = Symbol('A predicate that matches all records'); export class Predicates { public static get ALL(): typeof PredicateAll { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const predicate = >(c => c); predicatesAllSet.add(predicate); - return (predicate); + return predicate as unknown as typeof PredicateAll; } } @@ -122,7 +129,7 @@ export class ModelPredicateCreator { * @param predicate The storage predicate (lookup key) to test. */ static isValidPredicate( - predicate: any + predicate: any, ): predicate is ModelPredicate { return ModelPredicateCreator.predicateGroupsMap.has(predicate); } @@ -140,7 +147,7 @@ export class ModelPredicateCreator { */ static getPredicates( predicate: ModelPredicate, - throwOnInvalid: boolean = true + throwOnInvalid = true, ) { if (throwOnInvalid && !ModelPredicateCreator.isValidPredicate(predicate)) { throw new Error('The predicate is not valid'); @@ -159,7 +166,7 @@ export class ModelPredicateCreator { */ static createForPk( modelDefinition: SchemaModel, - model: T + model: T, ) { const keyFields = extractPrimaryKeyFieldNames(modelDefinition); const keyValues = extractPrimaryKeyValues(model, keyFields); @@ -167,6 +174,7 @@ export class ModelPredicateCreator { const predicate = this.createFromAST(modelDefinition, { and: keyFields.map((field, idx) => { const operand = keyValues[idx]; + return { [field]: { eq: operand } }; }), }); @@ -185,11 +193,12 @@ export class ModelPredicateCreator { */ static createFromFlatEqualities( modelDefinition: SchemaModel, - flatEqualities: Record + flatEqualities: Record, ) { const ast = { and: Object.entries(flatEqualities).map(([k, v]) => ({ [k]: { eq: v } })), }; + return this.createFromAST(modelDefinition, ast); } @@ -217,7 +226,7 @@ export class ModelPredicateCreator { static transformGraphQLFilterNodeToPredicateAST(gql: any) { if (!isValid(gql)) { throw new Error( - 'Invalid GraphQL Condition or subtree: ' + JSON.stringify(gql) + 'Invalid GraphQL Condition or subtree: ' + JSON.stringify(gql), ); } @@ -229,14 +238,16 @@ export class ModelPredicateCreator { } else if (isGroup(gql)) { const groupkey = Object.keys(gql)[0]; const children = this.transformGraphQLFilterNodeToPredicateAST( - gql[groupkey] + gql[groupkey], ); + return { type: groupkey, predicates: Array.isArray(children) ? children : [children], }; } else if (isComparison(gql)) { const operatorKey = Object.keys(gql)[0]; + return { operator: operatorKey, operand: gql[operatorKey], @@ -246,6 +257,7 @@ export class ModelPredicateCreator { return gql.map(o => this.transformGraphQLFilterNodeToPredicateAST(o)); } else { const fieldKey = Object.keys(gql)[0]; + return { field: fieldKey, ...this.transformGraphQLFilterNodeToPredicateAST(gql[fieldKey]), @@ -278,13 +290,13 @@ export class ModelPredicateCreator { */ static createFromAST( modelDefinition: SchemaModel, - ast: any + ast: any, ): ModelPredicate { const key = {} as ModelPredicate; ModelPredicateCreator.predicateGroupsMap.set( key, - this.transformGraphQLFilterNodeToPredicateAST(ast) + this.transformGraphQLFilterNodeToPredicateAST(ast), ); return key; diff --git a/packages/datastore/src/predicates/next.ts b/packages/datastore/src/predicates/next.ts index b4acf3642ea..c08eed7a42c 100644 --- a/packages/datastore/src/predicates/next.ts +++ b/packages/datastore/src/predicates/next.ts @@ -1,38 +1,38 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - PersistentModel, + AllFieldOperators, ModelFieldType, ModelMeta, - ModelPredicate as StoragePredicate, - AllFieldOperators, - PredicateInternalsKey, V5ModelPredicate as ModelPredicate, + PersistentModel, + PredicateInternalsKey, RecursiveModelPredicate, - RecursiveModelPredicateExtender, RecursiveModelPredicateAggregateExtender, -} from '../types'; + RecursiveModelPredicateExtender, + ModelPredicate as StoragePredicate, +} from '~/src/types'; +import { ExclusiveStorage as StorageAdapter } from '~/src/storage/storage'; +import { ModelRelationship } from '~/src/storage/relationship'; +import { asyncEvery, asyncSome } from '~/src/util'; import { ModelPredicateCreator as FlatModelPredicateCreator, comparisonKeys, } from './index'; -import { ExclusiveStorage as StorageAdapter } from '../storage/storage'; -import { ModelRelationship } from '../storage/relationship'; -import { asyncSome, asyncEvery } from '../util'; const ops = [...comparisonKeys] as AllFieldOperators[]; type GroupOperator = 'and' | 'or' | 'not'; -type UntypedCondition = { - fetch: (storage: StorageAdapter) => Promise[]>; - matches: (item: Record) => Promise; +interface UntypedCondition { + fetch(storage: StorageAdapter): Promise[]>; + matches(item: Record): Promise; copy( - extract?: GroupCondition + extract?: GroupCondition, ): [UntypedCondition, GroupCondition | undefined]; toAST(): any; -}; +} /** * A map from keys (exposed to customers) to the internal predicate data @@ -52,6 +52,7 @@ const predicateInternalsMap = new Map(); const registerPredicateInternals = (condition: GroupCondition, key?: any) => { const finalKey = key || new PredicateInternalsKey(); predicateInternalsMap.set(finalKey, condition); + return finalKey; }; @@ -69,9 +70,10 @@ const registerPredicateInternals = (condition: GroupCondition, key?: any) => { export const internals = (key: any) => { if (!predicateInternalsMap.has(key)) { throw new Error( - "Invalid predicate. Terminate your predicate with a valid condition (e.g., `p => p.field.eq('value')`) or pass `Predicates.ALL`." + "Invalid predicate. Terminate your predicate with a valid condition (e.g., `p => p.field.eq('value')`) or pass `Predicates.ALL`.", ); } + return predicateInternalsMap.get(key)!; }; @@ -103,7 +105,7 @@ export class FieldCondition { constructor( public field: string, public operator: string, - public operands: string[] + public operands: string[], ) { this.validate(); } @@ -113,7 +115,7 @@ export class FieldCondition { * @param extract Not used. Present only to fulfill the `UntypedCondition` interface. * @returns A new, identitical `FieldCondition`. */ - copy(extract?: GroupCondition): [FieldCondition, GroupCondition | undefined] { + copy(_?: GroupCondition): [FieldCondition, GroupCondition | undefined] { return [ new FieldCondition(this.field, this.operator, [...this.operands]), undefined, @@ -180,7 +182,7 @@ export class FieldCondition { return new FieldCondition( this.field, negations[this.operator], - this.operands + this.operands, ); } } @@ -191,8 +193,8 @@ export class FieldCondition { * @param storage N/A. If ever implemented, the storage adapter to query. * @returns N/A. If ever implemented, return items from `storage` that match. */ - async fetch(storage: StorageAdapter): Promise[]> { - return Promise.reject('No implementation needed [yet].'); + async fetch(_: StorageAdapter): Promise[]> { + return Promise.reject(new Error('No implementation needed [yet].')); } /** @@ -217,6 +219,7 @@ export class FieldCondition { const operation = operations[this.operator as keyof typeof operations]; if (operation) { const result = operation(); + return result; } else { throw new Error(`Invalid operator given: ${this.operator}`); @@ -234,6 +237,7 @@ export class FieldCondition { */ const argumentCount = count => { const argsClause = count === 1 ? 'argument is' : 'arguments are'; + return () => { if (this.operands.length !== count) { return `Exactly ${count} ${argsClause} required.`; @@ -278,6 +282,7 @@ export class FieldCondition { */ const getGroupId = (() => { let seed = 1; + return () => `group_${seed++}`; })(); @@ -345,7 +350,7 @@ export class GroupCondition { * This is used to guard against infinitely fetch -> optimize -> fetch * recursion. */ - public isOptimized: boolean = false + public isOptimized = false, ) {} /** @@ -360,7 +365,7 @@ export class GroupCondition { this.field, this.relationshipType, this.operator, - [] + [], ); let extractedCopy: GroupCondition | undefined = @@ -386,6 +391,7 @@ export class GroupCondition { */ withFieldConditionsOnly(negate: boolean) { const negateChildren = negate !== (this.operator === 'not'); + return new GroupCondition( this.model, undefined, @@ -397,8 +403,8 @@ export class GroupCondition { this.operands .filter(o => o instanceof FieldCondition) .map(o => - negateChildren ? (o as FieldCondition).negated(this.model) : o - ) + negateChildren ? (o as FieldCondition).negated(this.model) : o, + ), ); } @@ -440,7 +446,7 @@ export class GroupCondition { */ optimized(preserveNode = true): UntypedCondition { const operands = this.operands.map(o => - o instanceof GroupCondition ? o.optimized(this.operator === 'not') : o + o instanceof GroupCondition ? o.optimized(this.operator === 'not') : o, ); // we're only collapsing and/or groups that contains a single child for now, @@ -474,7 +480,7 @@ export class GroupCondition { this.relationshipType, this.operator, operands, - true + true, ); } @@ -489,13 +495,13 @@ export class GroupCondition { async fetch( storage: StorageAdapter, breadcrumb: string[] = [], - negate = false + negate = false, ): Promise[]> { if (!this.isOptimized) { return this.optimized().fetch(storage); } - const resultGroups: Array[]> = []; + const resultGroups: Record[][] = []; const operator = (negate ? negations[this.operator] : this.operator) as | 'or' @@ -512,21 +518,21 @@ export class GroupCondition { * candidate results are selected to match those. */ const groups = this.operands.filter( - op => op instanceof GroupCondition + op => op instanceof GroupCondition, ) as GroupCondition[]; /** * Simple conditions that must match the target model of `this`. */ const conditions = this.operands.filter( - op => op instanceof FieldCondition + op => op instanceof FieldCondition, ) as FieldCondition[]; for (const g of groups) { const relatives = await g.fetch( storage, [...breadcrumb, this.groupId], - negateChildren + negateChildren, ); // no relatives -> no need to attempt to perform a "join" query for @@ -564,7 +570,7 @@ export class GroupCondition { const relationship = ModelRelationship.from(this.model, g.field); - type JoinCondition = { [x: string]: { eq: any } }; + type JoinCondition = Record; if (relationship) { const allJoinConditions: { and: JoinCondition[] }[] = []; for (const relative of relatives) { @@ -583,7 +589,7 @@ export class GroupCondition { this.model.schema, { or: allJoinConditions, - } + }, ); resultGroups.push(await storage.query(this.model.builder, predicate)); @@ -627,7 +633,7 @@ export class GroupCondition { resultIndex = new Map(group.map(item => [getPKValue(item), item])); } else { const intersectWith = new Map>( - group.map(item => [getPKValue(item), item]) + group.map(item => [getPKValue(item), item]), ); for (const k of resultIndex.keys()) { if (!intersectWith.has(k)) { @@ -665,7 +671,7 @@ export class GroupCondition { */ async matches( item: Record, - ignoreFieldName: boolean = false + ignoreFieldName = false, ): Promise { const itemToCheck = this.field && !ignoreFieldName ? await item[this.field] : item; @@ -686,6 +692,7 @@ export class GroupCondition { return true; } } + return false; } @@ -696,9 +703,10 @@ export class GroupCondition { } else if (this.operator === 'not') { if (this.operands.length !== 1) { throw new Error( - 'Invalid arguments! `not()` accepts exactly one predicate expression.' + 'Invalid arguments! `not()` accepts exactly one predicate expression.', ); } + return !(await this.operands[0].matches(itemToCheck)); } else { throw new Error('Invalid group operator!'); @@ -725,7 +733,7 @@ export class GroupCondition { toStoragePredicate(): StoragePredicate { return FlatModelPredicateCreator.createFromAST( this.model.schema, - this.toAST() + this.toAST(), ) as StoragePredicate; } @@ -769,10 +777,10 @@ export class GroupCondition { */ export function recursivePredicateFor( ModelType: ModelMeta, - allowRecursion: boolean = true, + allowRecursion = true, field?: string, query?: GroupCondition, - tail?: GroupCondition + tail?: GroupCondition, ): RecursiveModelPredicate & PredicateInternalsKey { // to be used if we don't have a base query or tail to build onto const starter = new GroupCondition(ModelType, field, undefined, 'and', []); @@ -788,15 +796,16 @@ export function recursivePredicateFor( registerPredicateInternals(baseCondition, link); const copyLink = () => { - const [query, newTail] = baseCondition.copy(tailCondition); + const [copiedQuery, newTail] = baseCondition.copy(tailCondition); const newLink = recursivePredicateFor( ModelType, allowRecursion, undefined, - query, - newTail + copiedQuery, + newTail, ); - return { query, newTail, newLink }; + + return { query: copiedQuery, newTail, newLink }; }; // Adds .or() and .and() methods to the link. @@ -805,14 +814,14 @@ export function recursivePredicateFor( link[op] = (builder: RecursiveModelPredicateAggregateExtender) => { // or() and and() will return a copy of the original link // to head off mutability concerns. - const { query, newTail } = copyLink(); + const { query: copiedQuery, newTail } = copyLink(); const childConditions = builder( - recursivePredicateFor(ModelType, allowRecursion) + recursivePredicateFor(ModelType, allowRecursion), ); if (!Array.isArray(childConditions)) { throw new Error( - `Invalid predicate. \`${op}\` groups must return an array of child conditions.` + `Invalid predicate. \`${op}\` groups must return an array of child conditions.`, ); } @@ -824,22 +833,22 @@ export function recursivePredicateFor( field, undefined, op as 'and' | 'or', - childConditions.map(c => internals(c)) - ) + childConditions.map(c => internals(c)), + ), ); // FinalPredicate - return registerPredicateInternals(query); + return registerPredicateInternals(copiedQuery); }; }); // TODO: If revisiting this code, consider proxy. link.not = ( - builder: RecursiveModelPredicateExtender + builder: RecursiveModelPredicateExtender, ): PredicateInternalsKey => { // not() will return a copy of the original link // to head off mutability concerns. - const { query, newTail } = copyLink(); + const { query: copiedQuery, newTail } = copyLink(); // unlike and() and or(), the customer will supply a "singular" child predicate. // the difference being: not() does not accept an array of predicate-like objects. @@ -847,13 +856,13 @@ export function recursivePredicateFor( newTail?.operands.push( new GroupCondition(ModelType, field, undefined, 'not', [ internals(builder(recursivePredicateFor(ModelType, allowRecursion))), - ]) + ]), ); // A `FinalModelPredicate`. // Return a thing that can no longer be extended, but instead used to `async filter(items)` // or query storage: `.__query.fetch(storage)`. - return registerPredicateInternals(query); + return registerPredicateInternals(copiedQuery); }; // For each field on the model schema, we want to add a getter @@ -881,31 +890,31 @@ export function recursivePredicateFor( [operator]: (...operands: any[]) => { // build off a fresh copy of the existing `link`, just in case // the same link is being used elsewhere by the customer. - const { query, newTail } = copyLink(); + const { query: copiedQuery, newTail } = copyLink(); // normalize operands. if any of the values are `undefiend`, use // `null` instead, because that's what will be stored cross-platform. const normalizedOperands = operands.map(o => - o === undefined ? null : o + o === undefined ? null : o, ); // add the given condition to the link's TAIL node. // remember: the base link might go N nodes deep! e.g., newTail?.operands.push( - new FieldCondition(fieldName, operator, normalizedOperands) + new FieldCondition(fieldName, operator, normalizedOperands), ); // A `FinalModelPredicate`. // Return a thing that can no longer be extended, but instead used to `async filter(items)` // or query storage: `.__query.fetch(storage)`. - return registerPredicateInternals(query); + return registerPredicateInternals(copiedQuery); }, }; }, {}); } else { if (!allowRecursion) { throw new Error( - 'Predication on releated models is not supported in this context.' + 'Predication on releated models is not supported in this context.', ); } else if ( def.association.connectionType === 'BELONGS_TO' || @@ -918,7 +927,7 @@ export function recursivePredicateFor( const relatedMeta = (def.type as ModelFieldType).modelConstructor; if (!relatedMeta) { throw new Error( - 'Related model metadata is missing. This is a bug! Please report it.' + 'Related model metadata is missing. This is a bug! Please report it.', ); } @@ -931,7 +940,7 @@ export function recursivePredicateFor( fieldName, def.association.connectionType, 'and', - [] + [], ); // `oldtail` here refers to the *copy* of the old tail. @@ -943,12 +952,13 @@ export function recursivePredicateFor( allowRecursion, undefined, newquery, - newtail + newtail, ); + return newlink; } else { throw new Error( - "Related model definition doesn't have a typedef. This is a bug! Please report it." + "Related model definition doesn't have a typedef. This is a bug! Please report it.", ); } } @@ -960,7 +970,7 @@ export function recursivePredicateFor( } export function predicateFor( - ModelType: ModelMeta + ModelType: ModelMeta, ): ModelPredicate & PredicateInternalsKey { // the cast here is just a cheap way to reduce the surface area from // the recursive type. diff --git a/packages/datastore/src/predicates/sort.ts b/packages/datastore/src/predicates/sort.ts index 7a5ab34d1ff..ff8624e1672 100644 --- a/packages/datastore/src/predicates/sort.ts +++ b/packages/datastore/src/predicates/sort.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { PersistentModel, - SchemaModel, - SortPredicate, ProducerSortPredicate, + SchemaModel, SortDirection, + SortPredicate, SortPredicatesGroup, -} from '../types'; +} from '~/src/types'; export class ModelSortPredicateCreator { private static sortPredicateGroupsMap = new WeakMap< @@ -16,37 +16,34 @@ export class ModelSortPredicateCreator { >(); private static createPredicateBuilder( - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ) { const { name: modelName } = modelDefinition; const fieldNames = new Set(Object.keys(modelDefinition.fields)); - let handler: ProxyHandler>; - const predicate = new Proxy( - {} as SortPredicate, - (handler = { - get(_target, propertyKey, receiver: SortPredicate) { - const field = propertyKey as keyof T; + const predicate = new Proxy({} as SortPredicate, { + get(_target, propertyKey, receiver: SortPredicate) { + const field = propertyKey as keyof T; - if (!fieldNames.has(field)) { - throw new Error( - `Invalid field for model. field: ${String( - field - )}, model: ${modelName}` - ); - } + if (!fieldNames.has(field)) { + throw new Error( + `Invalid field for model. field: ${String( + field, + )}, model: ${modelName}`, + ); + } - const result = (sortDirection: SortDirection) => { - ModelSortPredicateCreator.sortPredicateGroupsMap - .get(receiver) - ?.push({ field, sortDirection }); + const result = (sortDirection: SortDirection) => { + ModelSortPredicateCreator.sortPredicateGroupsMap + .get(receiver) + ?.push({ field, sortDirection }); - return receiver; - }; - return result; - }, - }) - ); + return receiver; + }; + + return result; + }, + }); ModelSortPredicateCreator.sortPredicateGroupsMap.set(predicate, []); @@ -54,14 +51,14 @@ export class ModelSortPredicateCreator { } static isValidPredicate( - predicate: any + predicate: any, ): predicate is SortPredicate { return ModelSortPredicateCreator.sortPredicateGroupsMap.has(predicate); } static getPredicates( predicate: SortPredicate, - throwOnInvalid: boolean = true + throwOnInvalid = true, ): SortPredicatesGroup { if ( throwOnInvalid && @@ -82,14 +79,14 @@ export class ModelSortPredicateCreator { // transforms cb-style predicate into Proxy static createFromExisting( modelDefinition: SchemaModel, - existing: ProducerSortPredicate + existing: ProducerSortPredicate, ) { if (!existing || !modelDefinition) { return undefined; } return existing( - ModelSortPredicateCreator.createPredicateBuilder(modelDefinition) + ModelSortPredicateCreator.createPredicateBuilder(modelDefinition), ); } } diff --git a/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts b/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts index 07d535aed70..5ea223837cc 100644 --- a/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts +++ b/packages/datastore/src/storage/adapter/AsyncStorageAdapter.ts @@ -1,6 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import AsyncStorageDatabase from './AsyncStorageDatabase'; import { ModelInstanceMetadata, ModelPredicate, @@ -10,24 +9,31 @@ import { PersistentModelConstructor, PredicatesGroup, QueryOne, -} from '../../types'; +} from '~/src/types'; import { DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR, - traverseModel, - validatePredicate, + getIndexKeys, + getStorename, inMemoryPagination, keysEqual, - getStorename, - getIndexKeys, -} from '../../util'; + traverseModel, + validatePredicate, +} from '~/src/util'; + +import AsyncStorageDatabase from './AsyncStorageDatabase'; import { StorageAdapterBase } from './StorageAdapterBase'; export class AsyncStorageAdapter extends StorageAdapterBase { protected db!: AsyncStorageDatabase; // no-ops for this adapter - protected async preSetUpChecks() {} - protected async preOpCheck() {} + protected async preSetUpChecks() { + // no-op + } + + protected async preOpCheck() { + // no-op + } /** * Open AsyncStorage database @@ -40,6 +46,7 @@ export class AsyncStorageAdapter extends StorageAdapterBase { protected async initDb(): Promise { const db = new AsyncStorageDatabase(); await db.init(); + return db; } @@ -52,7 +59,7 @@ export class AsyncStorageAdapter extends StorageAdapterBase { async batchSave( modelConstructor: PersistentModelConstructor, - items: ModelInstanceMetadata[] + items: ModelInstanceMetadata[], ): Promise<[T, OpType][]> { if (items.length === 0) { return []; @@ -72,33 +79,38 @@ export class AsyncStorageAdapter extends StorageAdapterBase { model, this.schema.namespaces[namespaceName], this.modelInstanceCreator, - this.getModelConstructorByModelName + this.getModelConstructorByModelName, ); const keyValuesPath = this.getIndexKeyValuesPath(model); - const { instance } = connectedModels.find(({ instance }) => { - const instanceKeyValuesPath = this.getIndexKeyValuesPath(instance); - return keysEqual([instanceKeyValuesPath], [keyValuesPath]); - })!; + const { instance } = connectedModels.find( + ({ instance: connectedModelInstance }) => { + const instanceKeyValuesPath = this.getIndexKeyValuesPath( + connectedModelInstance, + ); + + return keysEqual([instanceKeyValuesPath], [keyValuesPath]); + }, + )!; batch.push(instance); } - return await this.db.batchSave(storeName, batch, keys); + return this.db.batchSave(storeName, batch, keys); } protected async _get(storeName: string, keyArr: string[]): Promise { const itemKeyValuesPath: string = keyArr.join( - DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR + DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR, ); - return await this.db.get(itemKeyValuesPath, storeName); + return (await this.db.get(itemKeyValuesPath, storeName)) as T; } async save( model: T, - condition?: ModelPredicate + condition?: ModelPredicate, ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> { const { storeName, connectionStoreNames, modelKeyValues } = this.saveMetadata(model); @@ -109,12 +121,15 @@ export class AsyncStorageAdapter extends StorageAdapterBase { const result: [T, OpType.INSERT | OpType.UPDATE][] = []; for await (const resItem of connectionStoreNames) { - const { storeName, item, instance, keys } = resItem; + const { storeName: resItemStoreName, item, instance, keys } = resItem; const itemKeyValues: string[] = keys.map(key => item[key]); - const fromDB = await this._get(storeName, itemKeyValues); - const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT; + const resItemFromDB = (await this._get( + resItemStoreName, + itemKeyValues, + )) as T; + const opType: OpType = resItemFromDB ? OpType.UPDATE : OpType.INSERT; if ( keysEqual(itemKeyValues, modelKeyValues) || @@ -122,21 +137,22 @@ export class AsyncStorageAdapter extends StorageAdapterBase { ) { await this.db.save( item, - storeName, + resItemStoreName, keys, - itemKeyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR) + itemKeyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR), ); result.push([instance, opType]); } } + return result; } async query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { const { storeName, @@ -151,45 +167,48 @@ export class AsyncStorageAdapter extends StorageAdapterBase { if (queryByKey) { const keyValues = queryByKey.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR); const record = await this.getByKey(storeName, keyValues); + return record ? [record] : []; } if (predicates) { const filtered = await this.filterOnPredicate(storeName, predicates); + return this.inMemoryPagination(filtered, pagination); } if (hasSort || hasPagination) { const all = await this.getAll(storeName); + return this.inMemoryPagination(all, pagination); } return this.getAll(storeName); })()) as T[]; - return await this.load(namespaceName, modelConstructor.name, records); + return this.load(namespaceName, modelConstructor.name, records); } private async getByKey( storeName: string, - keyValuePath: string + keyValuePath: string, ): Promise { - return await this.db.get(keyValuePath, storeName); + return (await this.db.get(keyValuePath, storeName)) as T; } private async getAll( - storeName: string + storeName: string, ): Promise { - return await this.db.getAll(storeName); + return this.db.getAll(storeName); } private async filterOnPredicate( storeName: string, - predicates: PredicatesGroup + predicates: PredicatesGroup, ) { const { predicates: predicateObjs, type } = predicates; - const all = await this.getAll(storeName); + const all = (await this.getAll(storeName)) as T[]; const filtered = predicateObjs ? all.filter(m => validatePredicate(m, type, predicateObjs)) @@ -200,23 +219,23 @@ export class AsyncStorageAdapter extends StorageAdapterBase { private inMemoryPagination( records: T[], - pagination?: PaginationInput + pagination?: PaginationInput, ): T[] { return inMemoryPagination(records, pagination); } async queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne = QueryOne.FIRST + firstOrLast: QueryOne = QueryOne.FIRST, ): Promise { const storeName = this.getStorenameForModel(modelConstructor); - const result = await this.db.getOne(firstOrLast, storeName); + const result = (await this.db.getOne(firstOrLast, storeName)) as T; return result && this.modelInstanceCreator(modelConstructor, result); } protected async deleteItem( - deleteQueue?: { storeName: string; items: T[] | IDBValidKey[] }[] + deleteQueue?: { storeName: string; items: T[] | IDBValidKey[] }[], ) { for await (const deleteItem of deleteQueue!) { const { storeName, items } = deleteItem; @@ -232,7 +251,7 @@ export class AsyncStorageAdapter extends StorageAdapterBase { } } - //#region platform-specific helper methods + // #region platform-specific helper methods /** * Retrieves concatenated primary key values from a model @@ -242,11 +261,11 @@ export class AsyncStorageAdapter extends StorageAdapterBase { */ private getIndexKeyValuesPath(model: T): string { return this.getIndexKeyValuesFromModel(model).join( - DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR + DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR, ); } - //#endregion + // #endregion } export default new AsyncStorageAdapter(); diff --git a/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts b/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts index 99fc190c4e1..631e9ab3862 100644 --- a/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts +++ b/packages/datastore/src/storage/adapter/AsyncStorageDatabase.ts @@ -7,12 +7,13 @@ import { PaginationInput, PersistentModel, QueryOne, -} from '../../types'; +} from '~/src/types'; import { DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR, indexNameFromKeys, monotonicUlidFactory, -} from '../../util'; +} from '~/src/util'; + import { createInMemoryStore } from './InMemoryStore'; const DB_NAME = '@AmplifyDatastore'; @@ -72,12 +73,12 @@ class AsyncStorageDatabase { if (id === undefined) { // It is an old entry (without ulid). Need to migrate to new key format - const id = ulidOrId; + const newId = ulidOrId; const newUlid = this.getMonotonicFactory(storeName)(); - const oldKey = this.getLegacyKeyForItem(storeName, id); - const newKey = this.getKeyForItem(storeName, id, newUlid); + const oldKey = this.getLegacyKeyForItem(storeName, newId); + const newKey = this.getKeyForItem(storeName, newId, newUlid); const item = await this.storage.getItem(oldKey); @@ -105,7 +106,7 @@ class AsyncStorageDatabase { item: T, storeName: string, keys: string[], - keyValuesPath: string + keyValuesPath: string, ) { const idxName = indexNameFromKeys(keys); @@ -126,7 +127,7 @@ class AsyncStorageDatabase { async batchSave( storeName: string, items: ModelInstanceMetadata[], - keys: string[] + keys: string[], ): Promise<[T, OpType][]> { if (items.length === 0) { return []; @@ -157,11 +158,11 @@ class AsyncStorageDatabase { const key = this.getKeyForItem( storeName, keyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR), - ulid + ulid, ); allItemsKeys.push(key); - itemsMap[key] = { ulid, model: (item) }; + itemsMap[key] = { ulid, model: item as unknown as T }; if (_deleted) { keysToDelete.add(key); @@ -180,6 +181,7 @@ class AsyncStorageDatabase { await new Promise((resolve, reject) => { if (keysToDelete.size === 0) { resolve(); + return; } @@ -208,6 +210,7 @@ class AsyncStorageDatabase { await new Promise((resolve, reject) => { if (keysToSave.size === 0) { resolve(); + return; } @@ -252,12 +255,13 @@ class AsyncStorageDatabase { async get( keyValuePath: string, - storeName: string + storeName: string, ): Promise { const ulid = this.getCollectionIndex(storeName)!.get(keyValuePath)!; const itemKey = this.getKeyForItem(storeName, keyValuePath, ulid); const recordAsString = await this.storage.getItem(itemKey); const record = recordAsString && JSON.parse(recordAsString); + return record; } @@ -267,14 +271,18 @@ class AsyncStorageDatabase { const [itemId, ulid] = firstOrLast === QueryOne.FIRST ? (() => { - let id: string, ulid: string; - for ([id, ulid] of collection) break; // Get first element of the set - return [id!, ulid!]; + let id: string, targetUlid: string; + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-unreachable-loop + for ([id, targetUlid] of collection) break; // Get first element of the set + + return [id!, targetUlid!]; })() : (() => { - let id: string, ulid: string; - for ([id, ulid] of collection); // Get last element of the set - return [id!, ulid!]; + let id: string, targetUlid: string; + for ([id, targetUlid] of collection); // Get last element of the set + + return [id!, targetUlid!]; })(); const itemKey = this.getKeyForItem(storeName, itemId, ulid); @@ -291,7 +299,7 @@ class AsyncStorageDatabase { */ async getAll( storeName: string, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { const collection = this.getCollectionIndex(storeName)!; diff --git a/packages/datastore/src/storage/adapter/InMemoryStore.ts b/packages/datastore/src/storage/adapter/InMemoryStore.ts index 0a7c463c2c2..c275ee0e85e 100644 --- a/packages/datastore/src/storage/adapter/InMemoryStore.ts +++ b/packages/datastore/src/storage/adapter/InMemoryStore.ts @@ -9,8 +9,12 @@ export class InMemoryStore { multiGet = async (keys: string[]) => { return keys.reduce( - (res, k) => (res.push([k, this.db.get(k)!]), res), - [] as [string, string][] + (res, k) => { + res.push([k, this.db.get(k)!]); + + return res; + }, + [] as [string, string][], ); }; diff --git a/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts b/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts index 0efe2964331..e67db509c33 100644 --- a/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts +++ b/packages/datastore/src/storage/adapter/IndexedDBAdapter.ts @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import * as idb from 'idb'; import { - isPredicateObj, - isPredicateGroup, ModelInstanceMetadata, ModelPredicate, OpType, @@ -13,19 +11,22 @@ import { PredicateObject, PredicatesGroup, QueryOne, -} from '../../types'; + isPredicateGroup, + isPredicateObj, +} from '~/src/types'; import { + getStorename, + inMemoryPagination, isPrivateMode, + isSafariCompatabilityMode, + keysEqual, traverseModel, validatePredicate, - inMemoryPagination, - keysEqual, - getStorename, - isSafariCompatabilityMode, -} from '../../util'; -import { StorageAdapterBase } from './StorageAdapterBase'; +} from '~/src/util'; import { ConsoleLogger } from '@aws-amplify/core'; +import { StorageAdapterBase } from './StorageAdapterBase'; + const logger = new ConsoleLogger('DataStore'); /** @@ -55,7 +56,7 @@ const DB_VERSION = 3; class IndexedDBAdapter extends StorageAdapterBase { protected db!: idb.IDBPDatabase; - private safariCompatabilityMode: boolean = false; + private safariCompatabilityMode = false; // checks are called by StorageAdapterBase class protected async preSetUpChecks() { @@ -77,7 +78,7 @@ class IndexedDBAdapter extends StorageAdapterBase { * @returns IDB Database instance */ protected async initDb(): Promise { - return await idb.openDB(this.dbName, DB_VERSION, { + return idb.openDB(this.dbName, DB_VERSION, { upgrade: async (db, oldVersion, newVersion, txn) => { // create new database if (oldVersion === 0) { @@ -90,7 +91,7 @@ class IndexedDBAdapter extends StorageAdapterBase { db, namespaceName, storeName, - modelName + modelName, ); }); }); @@ -124,7 +125,7 @@ class IndexedDBAdapter extends StorageAdapterBase { db, namespaceName, storeName, - modelName + modelName, ); let cursor = await origStore.openCursor(); @@ -162,7 +163,7 @@ class IndexedDBAdapter extends StorageAdapterBase { db, namespaceName, storeName, - modelName + modelName, ); }); }); @@ -171,8 +172,6 @@ class IndexedDBAdapter extends StorageAdapterBase { txn.abort(); throw error; } - - return; } }, }); @@ -180,7 +179,7 @@ class IndexedDBAdapter extends StorageAdapterBase { protected async _get( storeOrStoreName: idb.IDBPObjectStore | string, - keyArr: string[] + keyArr: string[], ): Promise { let index: idb.IDBPIndex; @@ -194,7 +193,7 @@ class IndexedDBAdapter extends StorageAdapterBase { const result = await index.get(this.canonicalKeyPath(keyArr)); - return result; + return result as T; } async clear(): Promise { @@ -209,7 +208,7 @@ class IndexedDBAdapter extends StorageAdapterBase { async save( model: T, - condition?: ModelPredicate + condition?: ModelPredicate, ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> { await this.checkPrivate(); @@ -218,7 +217,7 @@ class IndexedDBAdapter extends StorageAdapterBase { const tx = this.db.transaction( [storeName, ...Array.from(set.values())], - 'readwrite' + 'readwrite', ); const store = tx.objectStore(storeName); @@ -228,22 +227,22 @@ class IndexedDBAdapter extends StorageAdapterBase { const result: [T, OpType.INSERT | OpType.UPDATE][] = []; for await (const resItem of connectionStoreNames) { - const { storeName, item, instance, keys } = resItem; - const store = tx.objectStore(storeName); + const { storeName: resItemStoreName, item, instance, keys } = resItem; + const resItemStore = tx.objectStore(resItemStoreName); const itemKeyValues: string[] = keys.map(key => item[key]); - const fromDB = await this._get(store, itemKeyValues); - const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT; + const resItemFromDB = (await this._get(resItemStore, itemKeyValues)) as T; + const opType: OpType = resItemFromDB ? OpType.UPDATE : OpType.INSERT; if ( keysEqual(itemKeyValues, modelKeyValues) || opType === OpType.INSERT ) { - const key = await store + const key = await resItemStore .index('byPk') .getKey(this.canonicalKeyPath(itemKeyValues)); - await store.put(item, key); + await resItemStore.put(item, key); result.push([instance, opType]); } } @@ -255,7 +254,7 @@ class IndexedDBAdapter extends StorageAdapterBase { async query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { await this.checkPrivate(); const { @@ -281,16 +280,19 @@ class IndexedDBAdapter extends StorageAdapterBase { // if (queryByKey) { const record = await this.getByKey(storeName, queryByKey); + return record ? [record] : []; } if (predicates) { const filtered = await this.filterOnPredicate(storeName, predicates); + return this.inMemoryPagination(filtered, pagination); } if (hasSort) { const all = await this.getAll(storeName); + return this.inMemoryPagination(all, pagination); } @@ -301,12 +303,12 @@ class IndexedDBAdapter extends StorageAdapterBase { return this.getAll(storeName); })()) as T[]; - return await this.load(namespaceName, modelConstructor.name, records); + return this.load(namespaceName, modelConstructor.name, records); } async queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne = QueryOne.FIRST + firstOrLast: QueryOne = QueryOne.FIRST, ): Promise { await this.checkPrivate(); const storeName = this.getStorenameForModel(modelConstructor); @@ -316,14 +318,14 @@ class IndexedDBAdapter extends StorageAdapterBase { .objectStore(storeName) .openCursor(undefined, firstOrLast === QueryOne.FIRST ? 'next' : 'prev'); - const result = cursor ? cursor.value : undefined; + const result = cursor ? (cursor.value as T) : undefined; return result && this.modelInstanceCreator(modelConstructor, result); } async batchSave( modelConstructor: PersistentModelConstructor, - items: ModelInstanceMetadata[] + items: ModelInstanceMetadata[], ): Promise<[T, OpType][]> { await this.checkPrivate(); @@ -337,7 +339,7 @@ class IndexedDBAdapter extends StorageAdapterBase { const result: [T, OpType][] = []; const txn = this.db.transaction(storeName, 'readwrite'); - const store = txn.store; + const { store } = txn; for (const item of items) { const model = this.modelInstanceCreator(modelConstructor, item); @@ -347,7 +349,7 @@ class IndexedDBAdapter extends StorageAdapterBase { model, this.schema.namespaces[namespaceName], this.modelInstanceCreator, - this.getModelConstructorByModelName! + this.getModelConstructorByModelName!, ); const keyValues = this.getIndexKeyValuesFromModel(model); @@ -358,18 +360,23 @@ class IndexedDBAdapter extends StorageAdapterBase { const key = await index.getKey(this.canonicalKeyPath(keyValues)); if (!_deleted) { - const { instance } = connectedModels.find(({ instance }) => { - const instanceKeyValues = this.getIndexKeyValuesFromModel(instance); - return keysEqual(instanceKeyValues, keyValues); - })!; + const { instance } = connectedModels.find( + ({ instance: connectedModelInstance }) => { + const instanceKeyValues = this.getIndexKeyValuesFromModel( + connectedModelInstance, + ); + + return keysEqual(instanceKeyValues, keyValues); + }, + )!; result.push([ - (instance), + instance as unknown as T, key ? OpType.UPDATE : OpType.INSERT, ]); await store.put(instance, key); } else { - result.push([(item), OpType.DELETE]); + result.push([item as unknown as T, OpType.DELETE]); if (key) { await store.delete(key); @@ -386,7 +393,7 @@ class IndexedDBAdapter extends StorageAdapterBase { deleteQueue: { storeName: string; items: T[] | IDBValidKey[]; - }[] + }[], ) { const connectionStoreNames = deleteQueue!.map(({ storeName }) => { return storeName; @@ -419,16 +426,15 @@ class IndexedDBAdapter extends StorageAdapterBase { } } - //#region platform-specific helper methods + // #region platform-specific helper methods private async checkPrivate() { - const isPrivate = await isPrivateMode().then(isPrivate => { - return isPrivate; - }); + const isPrivate = await isPrivateMode().then(value => value); if (isPrivate) { logger.error("IndexedDB not supported in this browser's private mode"); + return Promise.reject( - "IndexedDB not supported in this browser's private mode" + new Error("IndexedDB not supported in this browser's private mode"), ); } else { return Promise.resolve(); @@ -455,6 +461,7 @@ class IndexedDBAdapter extends StorageAdapterBase { private getNamespaceAndModelFromStorename(storeName: string) { const [namespaceName, ...modelNameArr] = storeName.split('_'); + return { namespaceName, modelName: modelNameArr.join('_'), @@ -465,7 +472,7 @@ class IndexedDBAdapter extends StorageAdapterBase { db: idb.IDBPDatabase, namespaceName: string, storeName: string, - modelName: string + modelName: string, ): idb.IDBPObjectStore { const store = db.createObjectStore(storeName, { autoIncrement: true, @@ -483,15 +490,15 @@ class IndexedDBAdapter extends StorageAdapterBase { private async getByKey( storeName: string, - keyValue: string[] + keyValue: string[], ): Promise { - return await this._get(storeName, keyValue); + return (await this._get(storeName, keyValue)) as T; } private async getAll( - storeName: string + storeName: string, ): Promise { - return await this.db.getAll(storeName); + return this.db.getAll(storeName); } /** @@ -506,7 +513,7 @@ class IndexedDBAdapter extends StorageAdapterBase { private matchingIndexQueries( storeName: string, predicates: PredicateObject[], - transaction: idb.IDBPTransaction + transaction: idb.IDBPTransaction, ) { // could be expanded later to include `exec()` and a `cardinality` estimate? const queries: (() => Promise)[] = []; @@ -541,7 +548,7 @@ class IndexedDBAdapter extends StorageAdapterBase { .transaction(storeName) .objectStore(storeName) .index(name) - .getAll(this.canonicalKeyPath(matchingPredicateValues)) + .getAll(this.canonicalKeyPath(matchingPredicateValues)), ); } } @@ -552,7 +559,7 @@ class IndexedDBAdapter extends StorageAdapterBase { private async baseQueryIndex( storeName: string, predicates: PredicatesGroup, - transaction?: idb.IDBPTransaction | undefined + transaction?: idb.IDBPTransaction | undefined, ) { let { predicates: predicateObjs, type } = predicates; @@ -565,12 +572,14 @@ class IndexedDBAdapter extends StorageAdapterBase { isPredicateGroup(predicateObjs[0]) && (predicateObjs[0] as PredicatesGroup).type !== 'not' ) { - type = (predicateObjs[0] as PredicatesGroup).type; + const { type: predicatesGroupType } = + predicateObjs[0] as PredicatesGroup; + type = predicatesGroupType; predicateObjs = (predicateObjs[0] as PredicatesGroup).predicates; } const fieldPredicates = predicateObjs.filter( - p => isPredicateObj(p) && p.operator === 'eq' + p => isPredicateObj(p) && p.operator === 'eq', ) as PredicateObject[]; // several sub-queries could occur here. explicitly start a txn here to avoid @@ -596,12 +605,12 @@ class IndexedDBAdapter extends StorageAdapterBase { predicateObjs .filter(o => isPredicateGroup(o) && o.type === 'and') .map(o => - this.baseQueryIndex(storeName, o as PredicatesGroup, txn) - ) + this.baseQueryIndex(storeName, o as PredicatesGroup, txn), + ), ).then(queries => queries .filter(q => q.indexedQueries.length === 1) - .map(i => i.indexedQueries) + .map(i => i.indexedQueries), ); /** @@ -610,7 +619,7 @@ class IndexedDBAdapter extends StorageAdapterBase { const objectQueries = predicateObjs .filter(o => isPredicateObj(o)) .map(o => - this.matchingIndexQueries(storeName, [o as PredicateObject], txn) + this.matchingIndexQueries(storeName, [o as PredicateObject], txn), ); const indexedQueries = [...groupQueries, ...objectQueries] @@ -639,7 +648,7 @@ class IndexedDBAdapter extends StorageAdapterBase { indexedQueries: this.matchingIndexQueries( storeName, fieldPredicates, - txn + txn, ), }; } else { @@ -660,13 +669,13 @@ class IndexedDBAdapter extends StorageAdapterBase { private async filterOnPredicate( storeName: string, - predicates: PredicatesGroup + predicates: PredicatesGroup, ) { const { predicates: predicateObjs, type } = predicates; const { groupType, indexedQueries } = await this.baseQueryIndex( storeName, - predicates + predicates, ); // where we'll accumulate candidate results, which will be filtered at the end. @@ -702,7 +711,7 @@ class IndexedDBAdapter extends StorageAdapterBase { // nothing intelligent we can do with `not` groups unless or until we start // smashing comparison operators against indexes -- at which point we could // perform some reversal here. - candidateResults = await this.getAll(storeName); + candidateResults = (await this.getAll(storeName)) as T[]; } const filtered = predicateObjs @@ -714,14 +723,14 @@ class IndexedDBAdapter extends StorageAdapterBase { private inMemoryPagination( records: T[], - pagination?: PaginationInput + pagination?: PaginationInput, ): T[] { return inMemoryPagination(records, pagination); } private async enginePagination( storeName: string, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { let result: T[]; @@ -753,7 +762,7 @@ class IndexedDBAdapter extends StorageAdapterBase { result = pageResults; } else { - result = await this.db.getAll(storeName); + result = (await this.db.getAll(storeName)) as T[]; } return result; @@ -771,9 +780,10 @@ class IndexedDBAdapter extends StorageAdapterBase { if (this.safariCompatabilityMode) { return keyArr.length > 1 ? keyArr : keyArr[0]; } + return keyArr; }; - //#endregion + // #endregion } export default new IndexedDBAdapter(); diff --git a/packages/datastore/src/storage/adapter/StorageAdapterBase.ts b/packages/datastore/src/storage/adapter/StorageAdapterBase.ts index 119eaf530c3..7af0c62a7e3 100644 --- a/packages/datastore/src/storage/adapter/StorageAdapterBase.ts +++ b/packages/datastore/src/storage/adapter/StorageAdapterBase.ts @@ -1,11 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Adapter } from './index'; -import { ModelInstanceCreator } from '../../datastore/datastore'; -import { ModelPredicateCreator } from '../../predicates'; +import { ModelInstanceCreator } from '~/src/datastore/datastore'; +import { ModelPredicateCreator } from '~/src/predicates'; import { InternalSchema, - isPredicateObj, ModelInstanceMetadata, ModelPredicate, NamespaceResolver, @@ -16,22 +14,26 @@ import { PredicateObject, PredicatesGroup, QueryOne, -} from '../../types'; + isPredicateObj, +} from '~/src/types'; import { NAMESPACES, - getStorename, - getIndexKeys, + extractPrimaryKeyFieldNames, extractPrimaryKeyValues, + getIndexKeys, + getStorename, + isModelConstructor, traverseModel, validatePredicate, - isModelConstructor, - extractPrimaryKeyFieldNames, -} from '../../util'; -import type { IDBPDatabase, IDBPObjectStore } from 'idb'; -import type AsyncStorageDatabase from './AsyncStorageDatabase'; -import { ModelRelationship } from '../relationship'; +} from '~/src/util'; +import { ModelRelationship } from '~/src/storage/relationship'; import { ConsoleLogger } from '@aws-amplify/core'; +import { Adapter } from './index'; + +import type AsyncStorageDatabase from './AsyncStorageDatabase'; +import type { IDBPDatabase, IDBPObjectStore } from 'idb'; + const logger = new ConsoleLogger('DataStore'); const DB_NAME = 'amplify-datastore'; @@ -44,8 +46,9 @@ export abstract class StorageAdapterBase implements Adapter { protected modelInstanceCreator!: ModelInstanceCreator; protected getModelConstructorByModelName!: ( namsespaceName: NAMESPACES, - modelName: string + modelName: string, ) => PersistentModelConstructor; + protected initPromise!: Promise; protected resolve!: (value?: any) => void; protected reject!: (value?: any) => void; @@ -71,19 +74,20 @@ export abstract class StorageAdapterBase implements Adapter { modelInstanceCreator: ModelInstanceCreator, getModelConstructorByModelName: ( namsespaceName: NAMESPACES, - modelName: string + modelName: string, ) => PersistentModelConstructor, - sessionId?: string + sessionId?: string, ): Promise { await this.preSetUpChecks(); if (!this.initPromise) { - this.initPromise = new Promise((res, rej) => { - this.resolve = res; - this.reject = rej; + this.initPromise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; }); } else { await this.initPromise; + return; } if (sessionId) { @@ -113,23 +117,23 @@ export abstract class StorageAdapterBase implements Adapter { public abstract save( model: T, - condition?: ModelPredicate + condition?: ModelPredicate, ); public abstract query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise; public abstract queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne + firstOrLast: QueryOne, ): Promise; public abstract batchSave( modelConstructor: PersistentModelConstructor, - items: ModelInstanceMetadata[] + items: ModelInstanceMetadata[], ): Promise<[T, OpType][]>; /** @@ -137,7 +141,7 @@ export abstract class StorageAdapterBase implements Adapter { * @returns local DB table name */ protected getStorenameForModel( - modelConstructor: PersistentModelConstructor + modelConstructor: PersistentModelConstructor, ): string { const namespace = this.namespaceResolver(modelConstructor); const { name: modelName } = modelConstructor; @@ -151,7 +155,7 @@ export abstract class StorageAdapterBase implements Adapter { * @returns the record's primary key values */ protected getIndexKeyValuesFromModel( - model: T + model: T, ): string[] { const modelConstructor = Object.getPrototypeOf(model) .constructor as PersistentModelConstructor; @@ -159,7 +163,7 @@ export abstract class StorageAdapterBase implements Adapter { const keys = getIndexKeys( this.schema.namespaces[namespaceName], - modelConstructor.name + modelConstructor.name, ); return extractPrimaryKeyValues(model, keys); @@ -172,7 +176,7 @@ export abstract class StorageAdapterBase implements Adapter { * @param model */ protected saveMetadata( - model: T + model: T, ): { storeName: string; set: Set; @@ -189,20 +193,21 @@ export abstract class StorageAdapterBase implements Adapter { model, this.schema.namespaces[namespaceName], this.modelInstanceCreator, - this.getModelConstructorByModelName! + this.getModelConstructorByModelName!, ); const set = new Set(); const connectionStoreNames = Object.values(connectedModels).map( ({ modelName, item, instance }) => { - const storeName = getStorename(namespaceName, modelName); - set.add(storeName); + const connectedModelStoreName = getStorename(namespaceName, modelName); + set.add(connectedModelStoreName); const keys = getIndexKeys( this.schema.namespaces[namespaceName], - modelName + modelName, ); - return { storeName, item, instance, keys }; - } + + return { storeName: connectedModelStoreName, item, instance, keys }; + }, ); const modelKeyValues = this.getIndexKeyValuesFromModel(model); @@ -218,7 +223,7 @@ export abstract class StorageAdapterBase implements Adapter { */ protected validateSaveCondition( condition?: ModelPredicate, - fromDB?: unknown + fromDB?: unknown, ): void { if (!(condition && fromDB)) { return; @@ -239,7 +244,7 @@ export abstract class StorageAdapterBase implements Adapter { protected abstract _get( storeOrStoreName: IDBPObjectStore | string, - keyArr: string[] + keyArr: string[], ): Promise; /** @@ -253,7 +258,7 @@ export abstract class StorageAdapterBase implements Adapter { protected async load( namespaceName: NAMESPACES, srcModelName: string, - records: T[] + records: T[], ): Promise { const namespace = this.schema.namespaces[namespaceName]; const relations = namespace.relationships![srcModelName].relationTypes; @@ -262,17 +267,17 @@ export abstract class StorageAdapterBase implements Adapter { }); const modelConstructor = this.getModelConstructorByModelName!( namespaceName, - srcModelName + srcModelName, ); if (connectionStoreNames.length === 0) { return records.map(record => - this.modelInstanceCreator(modelConstructor, record) + this.modelInstanceCreator(modelConstructor, record), ); } return records.map(record => - this.modelInstanceCreator(modelConstructor, record) + this.modelInstanceCreator(modelConstructor, record), ); } @@ -295,7 +300,7 @@ export abstract class StorageAdapterBase implements Adapter { */ private keyValueFromPredicate( predicates: PredicatesGroup, - keyPath: string[] + keyPath: string[], ): string[] | undefined { const { predicates: predicateObjs } = predicates; @@ -314,7 +319,7 @@ export abstract class StorageAdapterBase implements Adapter { p.field === key && p.operator === 'eq' && p.operand !== null && - p.operand !== undefined + p.operand !== undefined, ) as PredicateObject; predicateObj && keyValues.push(predicateObj.operand); @@ -334,18 +339,18 @@ export abstract class StorageAdapterBase implements Adapter { protected queryMetadata( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ) { const storeName = this.getStorenameForModel(modelConstructor); const namespaceName = this.namespaceResolver( - modelConstructor + modelConstructor, ) as NAMESPACES; const predicates = predicate && ModelPredicateCreator.getPredicates(predicate); const keyPath = getIndexKeys( this.schema.namespaces[namespaceName], - modelConstructor.name + modelConstructor.name, ); const queryByKey = predicates && this.keyValueFromPredicate(predicates, keyPath); @@ -373,7 +378,7 @@ export abstract class StorageAdapterBase implements Adapter { */ public async delete( modelOrModelConstructor: T | PersistentModelConstructor, - condition?: ModelPredicate + condition?: ModelPredicate, ): Promise<[T[], T[]]> { await this.preOpCheck(); @@ -390,14 +395,14 @@ export abstract class StorageAdapterBase implements Adapter { models, modelConstructor, namespace, - deleteQueue + deleteQueue, ); await this.deleteItem(deleteQueue); const deletedModels = deleteQueue.reduce( (acc, { items }) => acc.concat(items), - [] + [] as T[], ); return [models, deletedModels]; @@ -406,14 +411,14 @@ export abstract class StorageAdapterBase implements Adapter { models, modelConstructor, namespace, - deleteQueue + deleteQueue, ); await this.deleteItem(deleteQueue); const deletedModels = deleteQueue.reduce( (acc, { items }) => acc.concat(items), - [] + [] as T[], ); return [models, deletedModels]; @@ -425,7 +430,7 @@ export abstract class StorageAdapterBase implements Adapter { .constructor as PersistentModelConstructor; const namespaceName = this.namespaceResolver( - modelConstructor + modelConstructor, ) as NAMESPACES; const storeName = this.getStorenameForModel(modelConstructor); @@ -457,21 +462,21 @@ export abstract class StorageAdapterBase implements Adapter { [model], modelConstructor, namespaceName, - deleteQueue + deleteQueue, ); } else { await this.deleteTraverse( [model], modelConstructor, namespaceName, - deleteQueue + deleteQueue, ); } await this.deleteItem(deleteQueue); const deletedModels = deleteQueue.reduce( (acc, { items }) => acc.concat(items), - [] + [] as T[], ); return [[model], deletedModels]; @@ -482,7 +487,7 @@ export abstract class StorageAdapterBase implements Adapter { deleteQueue?: { storeName: string; items: T[] | IDBValidKey[]; - }[] + }[], ); /** @@ -500,7 +505,7 @@ export abstract class StorageAdapterBase implements Adapter { models: T[], modelConstructor: PersistentModelConstructor, namespace: NAMESPACES, - deleteQueue: { storeName: string; items: T[] }[] + deleteQueue: { storeName: string; items: T[] }[], ): Promise { const cascadingRelationTypes = ['HAS_ONE', 'HAS_MANY']; @@ -515,7 +520,7 @@ export abstract class StorageAdapterBase implements Adapter { }; const relationships = ModelRelationship.allFrom(modelMeta).filter(r => - cascadingRelationTypes.includes(r.type) + cascadingRelationTypes.includes(r.type), ); for await (const r of relationships) { @@ -525,15 +530,15 @@ export abstract class StorageAdapterBase implements Adapter { r.remoteModelConstructor, ModelPredicateCreator.createFromFlatEqualities( r.remoteDefinition!, - queryObject - ) + queryObject, + ), ); await this.deleteTraverse( relatedRecords, r.remoteModelConstructor, namespace, - deleteQueue + deleteQueue, ); } } diff --git a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts index 95de512f4bd..76c4ab3fb8c 100644 --- a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts +++ b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.native.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Adapter } from '..'; -import AsyncStorageAdapter from '../AsyncStorageAdapter'; +import AsyncStorageAdapter from '~/src/storage/adapter/AsyncStorageAdapter'; const getDefaultAdapter: () => Adapter = () => { return AsyncStorageAdapter; diff --git a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts index d2e93163ba0..a73373e61c9 100644 --- a/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts +++ b/packages/datastore/src/storage/adapter/getDefaultAdapter/index.ts @@ -1,9 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Adapter } from '..'; -import IndexedDBAdapter from '../IndexedDBAdapter'; -import AsyncStorageAdapter from '../AsyncStorageAdapter'; -import { isWebWorker, isBrowser } from '@aws-amplify/core/internals/utils'; +import IndexedDBAdapter from '~/src/storage/adapter/IndexedDBAdapter'; +import AsyncStorageAdapter from '~/src/storage/adapter/AsyncStorageAdapter'; +import { isBrowser, isWebWorker } from '@aws-amplify/core/internals/utils'; + const getDefaultAdapter: () => Adapter = () => { if ((isBrowser && window.indexedDB) || (isWebWorker() && self.indexedDB)) { return IndexedDBAdapter as Adapter; diff --git a/packages/datastore/src/storage/adapter/index.ts b/packages/datastore/src/storage/adapter/index.ts index 8150a2f4a6f..e6f75f47bfa 100644 --- a/packages/datastore/src/storage/adapter/index.ts +++ b/packages/datastore/src/storage/adapter/index.ts @@ -9,29 +9,29 @@ import { PersistentModelConstructor, QueryOne, SystemComponent, -} from '../../types'; +} from '~/src/types'; export interface Adapter extends SystemComponent { clear(): Promise; save( model: T, - condition?: ModelPredicate + condition?: ModelPredicate, ): Promise<[T, OpType.INSERT | OpType.UPDATE][]>; - delete: ( + delete( modelOrModelConstructor: T | PersistentModelConstructor, - condition?: ModelPredicate - ) => Promise<[T[], T[]]>; + condition?: ModelPredicate, + ): Promise<[T[], T[]]>; query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise; queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne + firstOrLast: QueryOne, ): Promise; batchSave( modelConstructor: PersistentModelConstructor, - items: ModelInstanceMetadata[] + items: ModelInstanceMetadata[], ): Promise<[T, OpType][]>; } diff --git a/packages/datastore/src/storage/relationship.ts b/packages/datastore/src/storage/relationship.ts index 3d35d75f8d4..eee7c0e882a 100644 --- a/packages/datastore/src/storage/relationship.ts +++ b/packages/datastore/src/storage/relationship.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { isFieldAssociation, ModelFieldType, ModelMeta } from '../types'; +import { ModelFieldType, ModelMeta, isFieldAssociation } from '~/src/types'; /** * Defines a relationship from a LOCAL model.field to a REMOTE model.field and helps @@ -52,6 +52,7 @@ export class ModelRelationship { const relationship = ModelRelationship.from(model, field); relationship && relationships.push(relationship); } + return relationships; } @@ -212,7 +213,7 @@ export class ModelRelationship { // This case is theoretically unnecessary going forward. return [this.explicitRemoteAssociation.targetName!]; } else if (this.explicitRemoteAssociation?.targetNames) { - return this.explicitRemoteAssociation?.targetNames!; + return this.explicitRemoteAssociation.targetNames!; } else if (this.localAssociatedWith) { return this.localAssociatedWith; } else { @@ -249,6 +250,7 @@ export class ModelRelationship { for (let i = 0; i < this.localJoinFields.length; i++) { fk[this.localJoinFields[i]] = remote[this.remoteJoinFields[i]]; } + return fk; } @@ -278,6 +280,7 @@ export class ModelRelationship { if (localValue === null || localValue === undefined) return null; query[this.remoteJoinFields[i]] = local[this.localJoinFields[i]]; } + return query; } } diff --git a/packages/datastore/src/storage/storage.ts b/packages/datastore/src/storage/storage.ts index e9cd3f275fd..f5801c02056 100644 --- a/packages/datastore/src/storage/storage.ts +++ b/packages/datastore/src/storage/storage.ts @@ -1,11 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Observable, filter, map, Subject } from 'rxjs'; +import { Observable, Subject, filter, map } from 'rxjs'; import { Patch } from 'immer'; -import { ModelInstanceCreator } from '../datastore/datastore'; -import { ModelPredicateCreator } from '../predicates'; +import { ModelInstanceCreator } from '~/src/datastore/datastore'; +import { ModelPredicateCreator } from '~/src/predicates'; import { InternalSchema, + InternalSubscriptionMessage, ModelInstanceMetadata, ModelPredicate, NamespaceResolver, @@ -16,26 +17,26 @@ import { PredicatesGroup, QueryOne, SchemaNamespace, - InternalSubscriptionMessage, SubscriptionMessage, isTargetNameAssociation, -} from '../types'; +} from '~/src/types'; import { - isModelConstructor, + NAMESPACES, STORAGE, + isModelConstructor, validatePredicate, valuesEqual, - NAMESPACES, -} from '../util'; -import { getIdentifierValue } from '../sync/utils'; -import { Adapter } from './adapter'; -import getDefaultAdapter from './adapter/getDefaultAdapter'; +} from '~/src/util'; +import { getIdentifierValue } from '~/src/sync/utils'; import { Mutex } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { Adapter } from './adapter'; +import getDefaultAdapter from './adapter/getDefaultAdapter'; + export type StorageSubscriptionMessage = InternalSubscriptionMessage & { - mutator?: Symbol; + mutator?: symbol; }; export type StorageFacade = Omit; @@ -53,11 +54,11 @@ class StorageClass implements StorageFacade { private readonly namespaceResolver: NamespaceResolver, private readonly getModelConstructorByModelName: ( namsespaceName: NAMESPACES, - modelName: string + modelName: string, ) => PersistentModelConstructor, private readonly modelInstanceCreator: ModelInstanceCreator, private readonly adapter?: Adapter, - private readonly sessionId?: string + private readonly sessionId?: string, ) { this.adapter = this.adapter || getDefaultAdapter(); this.pushStream = new Subject(); @@ -78,6 +79,7 @@ class StorageClass implements StorageFacade { async init() { if (this.initialized !== undefined) { await this.initialized; + return; } logger.debug('Starting Storage'); @@ -85,9 +87,9 @@ class StorageClass implements StorageFacade { let resolve: (value?: void | PromiseLike) => void; let reject: (value?: void | PromiseLike) => void; - this.initialized = new Promise((res, rej) => { - resolve = res; - reject = rej; + this.initialized = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; }); this.adapter!.setUp( @@ -95,7 +97,7 @@ class StorageClass implements StorageFacade { this.namespaceResolver, this.modelInstanceCreator, this.getModelConstructorByModelName, - this.sessionId + this.sessionId, ).then(resolve!, reject!); await this.initialized; @@ -104,8 +106,8 @@ class StorageClass implements StorageFacade { async save( model: T, condition?: ModelPredicate, - mutator?: Symbol, - patchesTuple?: [Patch[], PersistentModel] + mutator?: symbol, + patchesTuple?: [Patch[], PersistentModel], ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> { await this.init(); if (!this.adapter) { @@ -142,7 +144,7 @@ class StorageClass implements StorageFacade { updateMutationInput = this.getChangedFieldsInput( model, savedElement, - patchesTuple + patchesTuple, ); // // an update without changed user fields // => don't create mutationEvent @@ -153,7 +155,7 @@ class StorageClass implements StorageFacade { const element = updateMutationInput || savedElement; - const modelConstructor = (Object.getPrototypeOf(savedElement) as Object) + const modelConstructor = (Object.getPrototypeOf(savedElement) as any) .constructor as PersistentModelConstructor; this.pushStream.next({ @@ -175,17 +177,19 @@ class StorageClass implements StorageFacade { delete( model: T, condition?: ModelPredicate, - mutator?: Symbol + mutator?: symbol, ): Promise<[T[], T[]]>; + delete( modelConstructor: PersistentModelConstructor, condition?: ModelPredicate, - mutator?: Symbol + mutator?: symbol, ): Promise<[T[], T[]]>; + async delete( modelOrModelConstructor: T | PersistentModelConstructor, condition?: ModelPredicate, - mutator?: Symbol + mutator?: symbol, ): Promise<[T[], T[]]> { await this.init(); if (!this.adapter) { @@ -197,7 +201,7 @@ class StorageClass implements StorageFacade { [models, deleted] = await this.adapter.delete( modelOrModelConstructor, - condition + condition, ); const modelConstructor = isModelConstructor(modelOrModelConstructor) @@ -212,8 +216,9 @@ class StorageClass implements StorageFacade { const modelIds = new Set( models.map(model => { const modelId = getIdentifierValue(modelDefinition, model); + return modelId; - }) + }), ); if ( @@ -224,7 +229,7 @@ class StorageClass implements StorageFacade { } deleted.forEach(model => { - const modelConstructor = (Object.getPrototypeOf(model) as Object) + const deletedModelConstructor = (Object.getPrototypeOf(model) as any) .constructor as PersistentModelConstructor; let theCondition: PredicatesGroup | undefined; @@ -237,7 +242,7 @@ class StorageClass implements StorageFacade { } this.pushStream.next({ - model: modelConstructor, + model: deletedModelConstructor, opType: OpType.DELETE, element: model, mutator, @@ -251,32 +256,32 @@ class StorageClass implements StorageFacade { async query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { await this.init(); if (!this.adapter) { throw new Error('Storage adapter is missing'); } - return await this.adapter.query(modelConstructor, predicate, pagination); + return this.adapter.query(modelConstructor, predicate, pagination); } async queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne = QueryOne.FIRST + firstOrLast: QueryOne = QueryOne.FIRST, ): Promise { await this.init(); if (!this.adapter) { throw new Error('Storage adapter is missing'); } - return await this.adapter.queryOne(modelConstructor, firstOrLast); + return this.adapter.queryOne(modelConstructor, firstOrLast); } observe( modelConstructor?: PersistentModelConstructor | null, predicate?: ModelPredicate | null, - skipOwn?: Symbol + skipOwn?: symbol, ): Observable> { const listenToAll = !modelConstructor; const { predicates, type } = @@ -287,13 +292,13 @@ class StorageClass implements StorageFacade { .pipe( filter(({ mutator }) => { return !skipOwn || mutator !== skipOwn; - }) + }), ) .pipe( map( ({ mutator: _mutator, ...message }) => - message as SubscriptionMessage - ) + message as SubscriptionMessage, + ), ); if (!listenToAll) { @@ -308,7 +313,7 @@ class StorageClass implements StorageFacade { } return true; - }) + }), ); } @@ -331,7 +336,7 @@ class StorageClass implements StorageFacade { async batchSave( modelConstructor: PersistentModelConstructor, items: ModelInstanceMetadata[], - mutator?: Symbol + mutator?: symbol, ): Promise<[T, OpType][]> { await this.init(); if (!this.adapter) { @@ -357,7 +362,7 @@ class StorageClass implements StorageFacade { private getChangedFieldsInput( model: T, originalElement: T, - patchesTuple?: [Patch[], PersistentModel] + patchesTuple?: [Patch[], PersistentModel], ): PersistentModel | null { const containsPatches = patchesTuple && patchesTuple.length; if (!containsPatches) { @@ -367,9 +372,9 @@ class StorageClass implements StorageFacade { const [patches, source] = patchesTuple!; const updatedElement = {}; // extract array of updated fields from patches - const updatedFields = ( - patches.map(patch => patch.path && patch.path[0]) - ); + const updatedFields = patches.map( + patch => patch.path && patch.path[0], + ) as string[]; // check model def for association and replace with targetName if exists const modelConstructor = Object.getPrototypeOf(model) @@ -383,7 +388,7 @@ class StorageClass implements StorageFacade { // set original values for these fields updatedFields.forEach((field: string) => { const targetNames: any = isTargetNameAssociation( - fields[field]?.association + fields[field]?.association, ); if (Array.isArray(targetNames)) { @@ -470,11 +475,11 @@ class ExclusiveStorage implements StorageFacade { namespaceResolver: NamespaceResolver, getModelConstructorByModelName: ( namsespaceName: NAMESPACES, - modelName: string + modelName: string, ) => PersistentModelConstructor, modelInstanceCreator: ModelInstanceCreator, adapter?: Adapter, - sessionId?: string + sessionId?: string, ) { this.storage = new StorageClass( schema, @@ -482,39 +487,41 @@ class ExclusiveStorage implements StorageFacade { getModelConstructorByModelName, modelInstanceCreator, adapter, - sessionId + sessionId, ); } runExclusive(fn: (storage: StorageClass) => Promise) { - return >this.mutex.runExclusive(fn.bind(this, this.storage)); + return this.mutex.runExclusive(fn.bind(this, this.storage)) as Promise; } async save( model: T, condition?: ModelPredicate, - mutator?: Symbol, - patchesTuple?: [Patch[], PersistentModel] + mutator?: symbol, + patchesTuple?: [Patch[], PersistentModel], ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> { return this.runExclusive<[T, OpType.INSERT | OpType.UPDATE][]>(storage => - storage.save(model, condition, mutator, patchesTuple) + storage.save(model, condition, mutator, patchesTuple), ); } async delete( model: T, condition?: ModelPredicate, - mutator?: Symbol + mutator?: symbol, ): Promise<[T[], T[]]>; + async delete( modelConstructor: PersistentModelConstructor, condition?: ModelPredicate, - mutator?: Symbol + mutator?: symbol, ): Promise<[T[], T[]]>; + async delete( modelOrModelConstructor: T | PersistentModelConstructor, condition?: ModelPredicate, - mutator?: Symbol + mutator?: symbol, ): Promise<[T[], T[]]> { return this.runExclusive<[T[], T[]]>(storage => { if (isModelConstructor(modelOrModelConstructor)) { @@ -532,19 +539,19 @@ class ExclusiveStorage implements StorageFacade { async query( modelConstructor: PersistentModelConstructor, predicate?: ModelPredicate, - pagination?: PaginationInput + pagination?: PaginationInput, ): Promise { return this.runExclusive(storage => - storage.query(modelConstructor, predicate, pagination) + storage.query(modelConstructor, predicate, pagination), ); } async queryOne( modelConstructor: PersistentModelConstructor, - firstOrLast: QueryOne = QueryOne.FIRST + firstOrLast: QueryOne = QueryOne.FIRST, ): Promise { return this.runExclusive(storage => - storage.queryOne(modelConstructor, firstOrLast) + storage.queryOne(modelConstructor, firstOrLast), ); } @@ -555,7 +562,7 @@ class ExclusiveStorage implements StorageFacade { observe( modelConstructor?: PersistentModelConstructor | null, predicate?: ModelPredicate | null, - skipOwn?: Symbol + skipOwn?: symbol, ): Observable> { return this.storage.observe(modelConstructor, predicate, skipOwn); } @@ -566,7 +573,7 @@ class ExclusiveStorage implements StorageFacade { batchSave( modelConstructor: PersistentModelConstructor, - items: ModelInstanceMetadata[] + items: ModelInstanceMetadata[], ): Promise<[T, OpType][]> { return this.storage.batchSave(modelConstructor, items); } diff --git a/packages/datastore/src/sync/datastoreConnectivity.ts b/packages/datastore/src/sync/datastoreConnectivity.ts index 17ce4bd1b75..10395ed753e 100644 --- a/packages/datastore/src/sync/datastoreConnectivity.ts +++ b/packages/datastore/src/sync/datastoreConnectivity.ts @@ -1,17 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observable, Observer, SubscriptionLike } from 'rxjs'; -import { ReachabilityMonitor } from './datastoreReachability'; -import { ConsoleLogger } from '@aws-amplify/core'; -const logger = new ConsoleLogger('DataStore'); +import { ReachabilityMonitor } from './datastoreReachability'; const RECONNECTING_IN = 5000; // 5s this may be configurable in the future -type ConnectionStatus = { +interface ConnectionStatus { // Might add other params in the future online: boolean; -}; +} export default class DataStoreConnectivity { private connectionStatus: ConnectionStatus; @@ -28,6 +26,7 @@ export default class DataStoreConnectivity { if (this.observer) { throw new Error('Subscriber already exists'); } + return new Observable(observer => { this.observer = observer; // Will be used to forward socket connection changes, enhancing Reachability @@ -57,7 +56,6 @@ export default class DataStoreConnectivity { // for consistency with other background processors. async stop() { this.unsubscribe(); - return; } socketDisconnected() { diff --git a/packages/datastore/src/sync/datastoreReachability/index.native.ts b/packages/datastore/src/sync/datastoreReachability/index.native.ts index d2993665f4b..8137ab74521 100644 --- a/packages/datastore/src/sync/datastoreReachability/index.native.ts +++ b/packages/datastore/src/sync/datastoreReachability/index.native.ts @@ -4,5 +4,5 @@ import { Reachability } from '@aws-amplify/core/internals/utils'; import { loadNetInfo } from '@aws-amplify/react-native'; export const ReachabilityMonitor = new Reachability().networkMonitor( - loadNetInfo() + loadNetInfo(), ); diff --git a/packages/datastore/src/sync/index.ts b/packages/datastore/src/sync/index.ts index e3b72b8cc1d..cc5d53d327c 100644 --- a/packages/datastore/src/sync/index.ts +++ b/packages/datastore/src/sync/index.ts @@ -1,68 +1,66 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { BackgroundProcessManager } from '@aws-amplify/core/internals/utils'; -import { Hub, ConsoleLogger } from '@aws-amplify/core'; - -import { filter, Observable, of, SubscriptionLike } from 'rxjs'; -import { ModelInstanceCreator } from '../datastore/datastore'; -import { ModelPredicateCreator } from '../predicates'; -import { ExclusiveStorage as Storage } from '../storage/storage'; +import { ConsoleLogger, Hub } from '@aws-amplify/core'; +import { Observable, SubscriptionLike, filter, of } from 'rxjs'; +import { ModelInstanceCreator } from '~/src/datastore/datastore'; +import { ModelPredicateCreator } from '~/src/predicates'; +import { ExclusiveStorage as Storage } from '~/src/storage/storage'; import { + AmplifyContext, + AuthModeStrategy, ConflictHandler, ControlMessageType, ErrorHandler, InternalSchema, + ManagedIdentifier, ModelInit, ModelInstanceMetadata, + ModelPredicate, MutableModel, NamespaceResolver, OpType, + OptionallyManagedIdentifier, PersistentModel, PersistentModelConstructor, SchemaModel, SchemaNamespace, TypeConstructorMap, - ModelPredicate, - AuthModeStrategy, - ManagedIdentifier, - OptionallyManagedIdentifier, - AmplifyContext, -} from '../types'; -// tslint:disable:no-duplicate-imports -import type { __modelMeta__ } from '../types'; +} from '~/src/types'; +import type { __modelMeta__ } from '~/src/types'; +import { SYNC, USER, getNow } from '~/src/util'; +import { + ConnectionState, + CONNECTION_STATE_CHANGE as PUBSUB_CONNECTION_STATE_CHANGE, + CONTROL_MSG as PUBSUB_CONTROL_MSG, +} from '@aws-amplify/api-graphql'; -import { getNow, SYNC, USER } from '../util'; -import DataStoreConnectivity from './datastoreConnectivity'; import { ModelMerger } from './merger'; import { MutationEventOutbox } from './outbox'; import { MutationProcessor } from './processors/mutation'; import { CONTROL_MSG, SubscriptionProcessor } from './processors/subscription'; import { SyncProcessor } from './processors/sync'; import { + TransformerMutationType, createMutationInstanceFromModelOperation, getIdentifierValue, predicateToGraphQLCondition, - TransformerMutationType, } from './utils'; - -import { - CONTROL_MSG as PUBSUB_CONTROL_MSG, - ConnectionState, - CONNECTION_STATE_CHANGE as PUBSUB_CONNECTION_STATE_CHANGE, -} from '@aws-amplify/api-graphql'; +import DataStoreConnectivity from './datastoreConnectivity'; const logger = new ConsoleLogger('DataStore'); const ownSymbol = Symbol('sync'); -type StartParams = { +interface StartParams { fullSyncInterval: number; -}; +} export declare class MutationEvent { readonly [__modelMeta__]: { identifier: OptionallyManagedIdentifier; }; + public readonly id: string; public readonly model: string; public readonly operation: TransformerMutationType; @@ -72,7 +70,7 @@ export declare class MutationEvent { constructor(init: ModelInit); static copyOf( src: MutationEvent, - mutator: (draft: MutableModel) => void | MutationEvent + mutator: (draft: MutableModel) => void | MutationEvent, ): MutationEvent; } @@ -80,6 +78,7 @@ export declare class ModelMetadata { readonly [__modelMeta__]: { identifier: ManagedIdentifier; }; + public readonly id: string; public readonly namespace: string; public readonly model: string; @@ -90,7 +89,7 @@ export declare class ModelMetadata { constructor(init: ModelInit); static copyOf( src: ModelMetadata, - mutator: (draft: MutableModel) => void | ModelMetadata + mutator: (draft: MutableModel) => void | ModelMetadata, ): ModelMetadata; } @@ -116,22 +115,24 @@ export class SyncEngine { private readonly modelMerger: ModelMerger; private readonly outbox: MutationEventOutbox; private readonly datastoreConnectivity: DataStoreConnectivity; - private readonly modelSyncedStatus: WeakMap< + private readonly modelSyncedStatus = new WeakMap< PersistentModelConstructor, boolean - > = new WeakMap(); + >(); + private unsleepSyncQueriesObservable: (() => void) | null; private waitForSleepState: Promise; private syncQueriesObservableStartSleeping: ( - value?: void | PromiseLike + value?: void | PromiseLike, ) => void; + private stopDisruptionListener: () => void; private connectionDisrupted = false; private runningProcesses: BackgroundProcessManager; public getModelSyncedStatus( - modelConstructor: PersistentModelConstructor + modelConstructor: PersistentModelConstructor, ): boolean { return this.modelSyncedStatus.get(modelConstructor)!; } @@ -152,22 +153,21 @@ export class SyncEngine { private readonly amplifyConfig: Record = {}, private readonly authModeStrategy: AuthModeStrategy, private readonly amplifyContext: AmplifyContext, - private readonly connectivityMonitor?: DataStoreConnectivity + private readonly connectivityMonitor?: DataStoreConnectivity, ) { this.runningProcesses = new BackgroundProcessManager(); this.waitForSleepState = new Promise(resolve => { this.syncQueriesObservableStartSleeping = resolve; }); - const MutationEvent = this.modelClasses[ - 'MutationEvent' - ] as PersistentModelConstructor; + const modelClassesMutationEvent = this.modelClasses + .MutationEvent as PersistentModelConstructor; this.outbox = new MutationEventOutbox( this.schema, - MutationEvent, + modelClassesMutationEvent, modelInstanceCreator, - ownSymbol + ownSymbol, ); this.modelMerger = new ModelMerger(this.outbox, ownSymbol); @@ -178,7 +178,7 @@ export class SyncEngine { this.amplifyConfig, this.authModeStrategy, errorHandler, - this.amplifyContext + this.amplifyContext, ); this.subscriptionsProcessor = new SubscriptionProcessor( @@ -187,7 +187,7 @@ export class SyncEngine { this.amplifyConfig, this.authModeStrategy, errorHandler, - this.amplifyContext + this.amplifyContext, ); this.mutationsProcessor = new MutationProcessor( @@ -196,12 +196,12 @@ export class SyncEngine { this.userModelClasses, this.outbox, this.modelInstanceCreator, - MutationEvent, + modelClassesMutationEvent, this.amplifyConfig, this.authModeStrategy, errorHandler, conflictHandler, - this.amplifyContext + this.amplifyContext, ); this.datastoreConnectivity = @@ -219,218 +219,215 @@ export class SyncEngine { await this.setupModels(params); } catch (err) { observer.error(err); + return; } // this is awaited at the bottom. so, we don't need to register // this explicitly with the context. it's already contained. - const startPromise = new Promise( - (doneStarting, failedStarting) => { - this.datastoreConnectivity.status().subscribe( - async ({ online }) => - this.runningProcesses.isOpen && - this.runningProcesses.add(async onTerminate => { - // From offline to online - if (online && !this.online) { - this.online = online; - - observer.next({ - type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, - data: { - active: this.online, - }, - }); - - let ctlSubsObservable: Observable; - let dataSubsObservable: Observable< + const startPromise = new Promise((resolve, reject) => { + this.datastoreConnectivity.status().subscribe( + async ({ online }) => + this.runningProcesses.isOpen && + this.runningProcesses.add(async onTerminate => { + // From offline to online + if (online && !this.online) { + this.online = online; + + observer.next({ + type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, + data: { + active: this.online, + }, + }); + + this.stopDisruptionListener = this.startDisruptionListener(); + // #region GraphQL Subscriptions + const [ctlSubsObservable, dataSubsObservable]: [ + Observable, + Observable< [TransformerMutationType, SchemaModel, PersistentModel] - >; - - this.stopDisruptionListener = - this.startDisruptionListener(); - //#region GraphQL Subscriptions - [ctlSubsObservable, dataSubsObservable] = - this.subscriptionsProcessor.start(); - - try { - await new Promise((resolve, reject) => { - onTerminate.then(reject); - const ctlSubsSubscription = ctlSubsObservable.subscribe( - { - next: msg => { - if (msg === CONTROL_MSG.CONNECTED) { - resolve(); - } - }, - error: err => { - reject(err); - const handleDisconnect = - this.disconnectionHandler(); - handleDisconnect(err); - }, + >, + ] = this.subscriptionsProcessor.start(); + + try { + await new Promise((_resolve, _reject) => { + onTerminate.then(_reject); + const ctlSubsSubscription = ctlSubsObservable.subscribe({ + next: msg => { + if (msg === CONTROL_MSG.CONNECTED) { + _resolve(); } - ); - - subscriptions.push(ctlSubsSubscription); + }, + error: err => { + _reject(err); + const handleDisconnect = this.disconnectionHandler(); + handleDisconnect(err); + }, }); - } catch (err) { - observer.error(err); - failedStarting(); - return; - } - logger.log('Realtime ready'); - - observer.next({ - type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED, + subscriptions.push(ctlSubsSubscription); }); + } catch (err) { + observer.error(err); + reject(err); - //#endregion + return; + } - //#region Base & Sync queries - try { - await new Promise((resolve, reject) => { - const syncQuerySubscription = - this.syncQueriesObservable().subscribe({ - next: message => { - const { type } = message; + logger.log('Realtime ready'); + + observer.next({ + type: ControlMessage.SYNC_ENGINE_SUBSCRIPTIONS_ESTABLISHED, + }); + + // #endregion + + // #region Base & Sync queries + try { + await new Promise((_resolve, _reject) => { + const syncQuerySubscription = + this.syncQueriesObservable().subscribe({ + next: message => { + const { type } = message; + + if ( + type === + ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY + ) { + _resolve(); + } + + observer.next(message); + }, + complete: () => { + _resolve(); + }, + error: error => { + _reject(error); + }, + }); - if ( - type === - ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY - ) { - resolve(); - } + if (syncQuerySubscription) { + subscriptions.push(syncQuerySubscription); + } + }); + } catch (error) { + observer.error(error); + reject(error); - observer.next(message); - }, - complete: () => { - resolve(); - }, - error: error => { - reject(error); + return; + } + // #endregion + + // #region process mutations (outbox) + subscriptions.push( + this.mutationsProcessor + .start() + .subscribe(({ modelDefinition, model: item, hasMore }) => + this.runningProcesses.add(async () => { + const modelConstructor = this.userModelClasses[ + modelDefinition.name + ] as PersistentModelConstructor; + + const model = this.modelInstanceCreator( + modelConstructor, + item, + ); + + await this.storage.runExclusive(storage => + this.modelMerger.merge( + storage, + model, + modelDefinition, + ), + ); + + observer.next({ + type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED, + data: { + model: modelConstructor, + element: model, }, }); - if (syncQuerySubscription) { - subscriptions.push(syncQuerySubscription); - } - }); - } catch (error) { - observer.error(error); - failedStarting(); - return; - } - //#endregion - - //#region process mutations (outbox) - subscriptions.push( - this.mutationsProcessor - .start() - .subscribe( - ({ modelDefinition, model: item, hasMore }) => - this.runningProcesses.add(async () => { - const modelConstructor = this.userModelClasses[ - modelDefinition.name - ] as PersistentModelConstructor; - - const model = this.modelInstanceCreator( - modelConstructor, - item - ); - - await this.storage.runExclusive(storage => - this.modelMerger.merge( - storage, - model, - modelDefinition - ) - ); - - observer.next({ - type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED, - data: { - model: modelConstructor, - element: model, - }, - }); - - observer.next({ - type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS, - data: { - isEmpty: !hasMore, - }, - }); - }, 'mutation processor event') - ) - ); - //#endregion - - //#region Merge subscriptions buffer - subscriptions.push( - dataSubsObservable!.subscribe( - ([_transformerMutationType, modelDefinition, item]) => - this.runningProcesses.add(async () => { - const modelConstructor = this.userModelClasses[ - modelDefinition.name - ] as PersistentModelConstructor; - - const model = this.modelInstanceCreator( - modelConstructor, - item - ); - - await this.storage.runExclusive(storage => - this.modelMerger.merge( - storage, - model, - modelDefinition - ) - ); - }, 'subscription dataSubsObservable event') - ) - ); - //#endregion - } else if (!online) { - this.online = online; - - observer.next({ - type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, - data: { - active: this.online, - }, - }); - - subscriptions.forEach(sub => sub.unsubscribe()); - subscriptions = []; - } + observer.next({ + type: ControlMessage.SYNC_ENGINE_OUTBOX_STATUS, + data: { + isEmpty: !hasMore, + }, + }); + }, 'mutation processor event'), + ), + ); + // #endregion + + // #region Merge subscriptions buffer + subscriptions.push( + dataSubsObservable!.subscribe( + ([_transformerMutationType, modelDefinition, item]) => + this.runningProcesses.add(async () => { + const modelConstructor = this.userModelClasses[ + modelDefinition.name + ] as PersistentModelConstructor; + + const model = this.modelInstanceCreator( + modelConstructor, + item, + ); + + await this.storage.runExclusive(storage => + this.modelMerger.merge( + storage, + model, + modelDefinition, + ), + ); + }, 'subscription dataSubsObservable event'), + ), + ); + // #endregion + } else if (!online) { + this.online = online; + + observer.next({ + type: ControlMessage.SYNC_ENGINE_NETWORK_STATUS, + data: { + active: this.online, + }, + }); + + subscriptions.forEach(sub => { + sub.unsubscribe(); + }); + subscriptions = []; + } - doneStarting(); - }, 'datastore connectivity event') - ); - } - ); + resolve(); + }, 'datastore connectivity event'), + ); + }); this.storage .observe(null, null, ownSymbol) .pipe( filter(({ model }) => { const modelDefinition = this.getModelDefinition(model); + return modelDefinition.syncable === true; - }) + }), ) .subscribe({ next: async ({ opType, model, element, condition }) => this.runningProcesses.add(async () => { const namespace = this.schema.namespaces[this.namespaceResolver(model)]; - const MutationEventConstructor = this.modelClasses[ - 'MutationEvent' - ] as PersistentModelConstructor; + const MutationEventConstructor = this.modelClasses + .MutationEvent as PersistentModelConstructor; const modelDefinition = this.getModelDefinition(model); const graphQLCondition = predicateToGraphQLCondition( condition!, - modelDefinition + modelDefinition, ); const mutationEvent = createMutationInstanceFromModelOperation( namespace.relationships!, @@ -440,7 +437,7 @@ export class SyncEngine { element, graphQLCondition, MutationEventConstructor, - this.modelInstanceCreator + this.modelInstanceCreator, ); await this.outbox.enqueue(this.storage, mutationEvent); @@ -492,23 +489,16 @@ export class SyncEngine { } private async getModelsMetadataWithNextFullSync( - currentTimeStamp: number + currentTimeStamp: number, ): Promise> { - const modelLastSync: Map = new Map( + const modelLastSync = new Map( ( await this.runningProcesses.add( () => this.getModelsMetadata(), - 'sync/index getModelsMetadataWithNextFullSync' + 'sync/index getModelsMetadataWithNextFullSync', ) ).map( - ({ - namespace, - model, - lastSync, - lastFullSync, - fullSyncInterval, - lastSyncPredicate, - }) => { + ({ namespace, model, lastSync, lastFullSync, fullSyncInterval }) => { const nextFullSync = lastFullSync! + fullSyncInterval; const syncFrom = !lastFullSync || nextFullSync < currentTimeStamp @@ -519,8 +509,8 @@ export class SyncEngine { this.schema.namespaces[namespace].models[model], [namespace, syncFrom!], ]; - } - ) + }, + ), ); return modelLastSync; @@ -541,17 +531,17 @@ export class SyncEngine { let terminated = false; while (!observer.closed && !terminated) { - const count: WeakMap< + const count = new WeakMap< PersistentModelConstructor, { new: number; updated: number; deleted: number; } - > = new WeakMap(); + >(); const modelLastSync = await this.getModelsMetadataWithNextFullSync( - Date.now() + Date.now(), ); const paginatingModels = new Set(modelLastSync.keys()); @@ -561,9 +551,11 @@ export class SyncEngine { let start: number; let syncDuration: number; let lastStartedAt: number; - await new Promise((resolve, reject) => { + await new Promise((resolve, _reject) => { if (!this.runningProcesses.isOpen) resolve(); - onTerminate.then(() => resolve()); + onTerminate.then(() => { + resolve(); + }); syncQueriesSubscription = this.syncQueriesProcessor .start(modelLastSync) .subscribe({ @@ -605,7 +597,7 @@ export class SyncEngine { const page = items.filter(item => { const itemId = getIdentifierValue( modelDefinition, - item + item, ); if (!idsInOutbox.has(itemId)) { @@ -613,6 +605,7 @@ export class SyncEngine { } oneByOne.push(item); + return false; }); @@ -622,7 +615,7 @@ export class SyncEngine { const opType = await this.modelMerger.merge( storage, item, - modelDefinition + modelDefinition, ); if (opType !== undefined) { @@ -635,8 +628,8 @@ export class SyncEngine { storage, modelConstructor, page, - modelDefinition - )) + modelDefinition, + )), ); const counts = count.get(modelConstructor)!; @@ -661,10 +654,10 @@ export class SyncEngine { if (done) { const { name: modelName } = modelDefinition; - //#region update last sync for type + // #region update last sync for type let modelMetadata = await this.getModelMetadata( namespace, - modelName + modelName, ); const { lastFullSync, fullSyncInterval } = modelMetadata; @@ -676,7 +669,7 @@ export class SyncEngine { ? lastFullSync! : Math.max( lastFullSyncStartedAt, - isFullSync ? startedAt : lastFullSync! + isFullSync ? startedAt : lastFullSync!, ); modelMetadata = ( @@ -692,9 +685,9 @@ export class SyncEngine { await this.storage.save( modelMetadata, undefined, - ownSymbol + ownSymbol, ); - //#endregion + // #endregion const counts = count.get(modelConstructor); @@ -751,8 +744,8 @@ export class SyncEngine { logger.debug( `Next fullSync in ${msNextFullSync / 1000} seconds. (${new Date( - Date.now() + msNextFullSync - )})` + Date.now() + msNextFullSync, + )})`, ); // TODO: create `BackgroundProcessManager.sleep()` ... but, need to put @@ -768,25 +761,31 @@ export class SyncEngine { // TLDR; this is a lot of complexity here for a sleep(), // but, it's not clear to me yet how to support an // extensible, centralized cancelable `sleep()` elegantly. - await this.runningProcesses.add(async onTerminate => { - let sleepTimer; - let unsleep; + await this.runningProcesses.add( + async runningProcessesOnTerminate => { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line unused-imports/no-unused-vars + let sleepTimer; + let unsleep; + + const sleep = new Promise(resolve => { + unsleep = resolve; + sleepTimer = setTimeout(unsleep, msNextFullSync); + }); - const sleep = new Promise(_unsleep => { - unsleep = _unsleep; - sleepTimer = setTimeout(unsleep, msNextFullSync); - }); + runningProcessesOnTerminate.then(() => { + terminated = true; + this.syncQueriesObservableStartSleeping(); + unsleep(); + }); - onTerminate.then(() => { - terminated = true; + this.unsleepSyncQueriesObservable = unsleep; this.syncQueriesObservableStartSleeping(); - unsleep(); - }); - this.unsleepSyncQueriesObservable = unsleep; - this.syncQueriesObservableStartSleeping(); - return sleep; - }, 'syncQueriesObservable sleep'); + return sleep; + }, + 'syncQueriesObservable sleep', + ); this.unsleepSyncQueriesObservable = null; this.waitForSleepState = new Promise(resolve => { @@ -874,7 +873,7 @@ export class SyncEngine { const modelMetadata = await this.getModelMetadata(namespace, model.name); const syncPredicate = ModelPredicateCreator.getPredicates( this.syncPredicates.get(model)!, - false + false, ); const lastSyncPredicate = syncPredicate ? JSON.stringify(syncPredicate) @@ -891,7 +890,7 @@ export class SyncEngine { lastSyncPredicate, }), undefined, - ownSymbol + ownSymbol, ); } else { const prevSyncPredicate = modelMetadata.lastSyncPredicate @@ -909,7 +908,7 @@ export class SyncEngine { draft.lastFullSync = null!; (draft.lastSyncPredicate as any) = lastSyncPredicate; } - }) + }), ); } @@ -927,36 +926,40 @@ export class SyncEngine { } private async getModelsMetadata(): Promise { - const ModelMetadata = this.modelClasses + const modelClassesModelMetadata = this.modelClasses .ModelMetadata as PersistentModelConstructor; - const modelsMetadata = await this.storage.query(ModelMetadata); + const modelsMetadata = await this.storage.query(modelClassesModelMetadata); return modelsMetadata; } private async getModelMetadata( namespace: string, - model: string + model: string, ): Promise { - const ModelMetadata = this.modelClasses + const modelClassesModelMetadata = this.modelClasses .ModelMetadata as PersistentModelConstructor; const predicate = ModelPredicateCreator.createFromAST( - this.schema.namespaces[SYNC].models[ModelMetadata.name], - { and: [{ namespace: { eq: namespace } }, { model: { eq: model } }] } + this.schema.namespaces[SYNC].models[modelClassesModelMetadata.name], + { and: [{ namespace: { eq: namespace } }, { model: { eq: model } }] }, ); - const [modelMetadata] = await this.storage.query(ModelMetadata, predicate, { - page: 0, - limit: 1, - }); + const [modelMetadata] = await this.storage.query( + modelClassesModelMetadata, + predicate, + { + page: 0, + limit: 1, + }, + ); return modelMetadata; } private getModelDefinition( - modelConstructor: PersistentModelConstructor + modelConstructor: PersistentModelConstructor, ): SchemaModel { const namespaceName = this.namespaceResolver(modelConstructor); @@ -1074,6 +1077,7 @@ export class SyncEngine { }, }, }; + return namespace; } @@ -1122,7 +1126,7 @@ export class SyncEngine { this.waitForSleepState.then(() => { // unsleepSyncQueriesObservable will be set if waitForSleepState has resolved this.unsleepSyncQueriesObservable!(); - }) + }), ) ); } diff --git a/packages/datastore/src/sync/merger.ts b/packages/datastore/src/sync/merger.ts index db947dfb07b..633e6283fc2 100644 --- a/packages/datastore/src/sync/merger.ts +++ b/packages/datastore/src/sync/merger.ts @@ -1,12 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Storage } from '../storage/storage'; +import { Storage } from '~/src/storage/storage'; import { ModelInstanceMetadata, OpType, PersistentModelConstructor, SchemaModel, -} from '../types'; +} from '~/src/types'; + import { MutationEventOutbox } from './outbox'; import { getIdentifierValue } from './utils'; @@ -14,7 +15,7 @@ import { getIdentifierValue } from './utils'; class ModelMerger { constructor( private readonly outbox: MutationEventOutbox, - private readonly ownSymbol: Symbol + private readonly ownSymbol: symbol, ) {} /** @@ -26,13 +27,13 @@ class ModelMerger { public async merge( storage: Storage, model: T, - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): Promise { let result: OpType; const mutationsForModel = await this.outbox.getForModel( storage, model, - modelDefinition + modelDefinition, ); const isDelete = model._deleted; @@ -53,9 +54,9 @@ class ModelMerger { storage: Storage, modelConstructor: PersistentModelConstructor, items: ModelInstanceMetadata[], - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): Promise<[ModelInstanceMetadata, OpType][]> { - const itemsMap: Map = new Map(); + const itemsMap = new Map(); for (const item of items) { // merge items by model id. Latest record for a given id remains. @@ -66,7 +67,7 @@ class ModelMerger { const page = [...itemsMap.values()]; - return await storage.batchSave(modelConstructor, page, this.ownSymbol); + return storage.batchSave(modelConstructor, page, this.ownSymbol); } } diff --git a/packages/datastore/src/sync/outbox.ts b/packages/datastore/src/sync/outbox.ts index e5a59dc61cc..c0e51571913 100644 --- a/packages/datastore/src/sync/outbox.ts +++ b/packages/datastore/src/sync/outbox.ts @@ -1,22 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { MutationEvent } from './index'; -import { ModelPredicateCreator } from '../predicates'; +import { ModelPredicateCreator } from '~/src/predicates'; import { ExclusiveStorage as Storage, - StorageFacade, Storage as StorageClass, -} from '../storage/storage'; -import { ModelInstanceCreator } from '../datastore/datastore'; + StorageFacade, +} from '~/src/storage/storage'; +import { ModelInstanceCreator } from '~/src/datastore/datastore'; import { InternalSchema, PersistentModel, PersistentModelConstructor, QueryOne, SchemaModel, -} from '../types'; -import { USER, SYNC, valuesEqual } from '../util'; -import { getIdentifierValue, TransformerMutationType } from './utils'; +} from '~/src/types'; +import { SYNC, USER, valuesEqual } from '~/src/util'; + +import { TransformerMutationType, getIdentifierValue } from './utils'; + +import { MutationEvent } from './index'; // TODO: Persist deleted ids // https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#outbox @@ -25,18 +27,18 @@ class MutationEventOutbox { constructor( private readonly schema: InternalSchema, - private readonly MutationEvent: PersistentModelConstructor, + private readonly MutationEventConstructor: PersistentModelConstructor, private readonly modelInstanceCreator: ModelInstanceCreator, - private readonly ownSymbol: Symbol + private readonly ownSymbol: symbol, ) {} public async enqueue( storage: Storage, - mutationEvent: MutationEvent + mutationEvent: MutationEvent, ): Promise { await storage.runExclusive(async s => { const mutationEventModelDefinition = - this.schema.namespaces[SYNC].models['MutationEvent']; + this.schema.namespaces[SYNC].models.MutationEvent; // `id` is the key for the record in the mutationEvent; // `modelId` is the key for the actual record that was mutated @@ -47,15 +49,16 @@ class MutationEventOutbox { { modelId: { eq: mutationEvent.modelId } }, { id: { ne: this.inProgressMutationEventId } }, ], - } + }, ); // Check if there are any other records with same id - const [first] = await s.query(this.MutationEvent, predicate); + const [first] = await s.query(this.MutationEventConstructor, predicate); // No other record with same modelId, so enqueue if (first === undefined) { await s.save(mutationEvent, undefined, this.ownSymbol); + return; } @@ -64,7 +67,7 @@ class MutationEventOutbox { if (first.operation === TransformerMutationType.CREATE) { if (incomingMutationType === TransformerMutationType.DELETE) { - await s.delete(this.MutationEvent, predicate); + await s.delete(this.MutationEventConstructor, predicate); } else { // first gets updated with the incoming mutation's data, condition intentionally skipped @@ -72,11 +75,11 @@ class MutationEventOutbox { // data loss, since update mutations only include changed fields const merged = this.mergeUserFields(first, mutationEvent); await s.save( - this.MutationEvent.copyOf(first, draft => { + this.MutationEventConstructor.copyOf(first, draft => { draft.data = merged.data; }), undefined, - this.ownSymbol + this.ownSymbol, ); } } else { @@ -89,7 +92,7 @@ class MutationEventOutbox { merged = this.mergeUserFields(first, mutationEvent); // delete all for model - await s.delete(this.MutationEvent, predicate); + await s.delete(this.MutationEventConstructor, predicate); } merged = merged! || mutationEvent; @@ -103,7 +106,7 @@ class MutationEventOutbox { public async dequeue( storage: StorageClass, record?: PersistentModel, - recordOp?: TransformerMutationType + recordOp?: TransformerMutationType, ): Promise { const head = await this.peek(storage); @@ -123,7 +126,10 @@ class MutationEventOutbox { * @param storage */ public async peek(storage: StorageFacade): Promise { - const head = await storage.queryOne(this.MutationEvent, QueryOne.FIRST); + const head = await storage.queryOne( + this.MutationEventConstructor, + QueryOne.FIRST, + ); this.inProgressMutationEventId = head ? head.id : undefined!; @@ -133,7 +139,7 @@ class MutationEventOutbox { public async getForModel( storage: StorageFacade, model: T, - userModelDefinition: SchemaModel + userModelDefinition: SchemaModel, ): Promise { const mutationEventModelDefinition = this.schema.namespaces[SYNC].models.MutationEvent; @@ -141,17 +147,17 @@ class MutationEventOutbox { const modelId = getIdentifierValue(userModelDefinition, model); const mutationEvents = await storage.query( - this.MutationEvent, + this.MutationEventConstructor, ModelPredicateCreator.createFromAST(mutationEventModelDefinition, { and: { modelId: { eq: modelId } }, - }) + }), ); return mutationEvents; } public async getModelIds(storage: StorageFacade): Promise> { - const mutationEvents = await storage.query(this.MutationEvent); + const mutationEvents = await storage.query(this.MutationEventConstructor); const result = new Set(); @@ -167,7 +173,7 @@ class MutationEventOutbox { storage: StorageClass, record: PersistentModel, head: PersistentModel, - recordOp: string + recordOp: string, ): Promise { if (head.operation !== recordOp) { return; @@ -201,10 +207,9 @@ class MutationEventOutbox { } const mutationEventModelDefinition = - this.schema.namespaces[SYNC].models['MutationEvent']; + this.schema.namespaces[SYNC].models.MutationEvent; - const userModelDefinition = - this.schema.namespaces['user'].models[head.model]; + const userModelDefinition = this.schema.namespaces.user.models[head.model]; const recordId = getIdentifierValue(userModelDefinition, record); @@ -215,12 +220,12 @@ class MutationEventOutbox { { modelId: { eq: recordId } }, { id: { ne: this.inProgressMutationEventId } }, ], - } + }, ); const outdatedMutations = await storage.query( - this.MutationEvent, - predicate + this.MutationEventConstructor, + predicate, ); if (!outdatedMutations.length) { @@ -232,26 +237,26 @@ class MutationEventOutbox { const newData = { ...oldData, _version, _lastChangedAt }; - return this.MutationEvent.copyOf(m, draft => { + return this.MutationEventConstructor.copyOf(m, draft => { draft.data = JSON.stringify(newData); }); }); - await storage.delete(this.MutationEvent, predicate); + await storage.delete(this.MutationEventConstructor, predicate); await Promise.all( - reconciledMutations.map( - async m => await storage.save(m, undefined, this.ownSymbol) - ) + reconciledMutations.map(async m => + storage.save(m, undefined, this.ownSymbol), + ), ); } private mergeUserFields( previous: MutationEvent, - current: MutationEvent + current: MutationEvent, ): MutationEvent { const { _version, _lastChangedAt, _deleted, ...previousData } = JSON.parse( - previous.data + previous.data, ); const { @@ -269,13 +274,13 @@ class MutationEventOutbox { ...currentData, }); - return this.modelInstanceCreator(this.MutationEvent, { + return this.modelInstanceCreator(this.MutationEventConstructor, { ...current, data, }); } - /* + /* if a model is using custom timestamp fields the custom field names will be stored in the model attributes @@ -294,7 +299,7 @@ class MutationEventOutbox { */ private removeTimestampFields( model: string, - record: PersistentModel + record: PersistentModel, ): PersistentModel { const CREATED_AT_DEFAULT_KEY = 'createdAt'; const UPDATED_AT_DEFAULT_KEY = 'updatedAt'; diff --git a/packages/datastore/src/sync/processors/errorMaps.ts b/packages/datastore/src/sync/processors/errorMaps.ts index b67a0de5cfb..aee562f5791 100644 --- a/packages/datastore/src/sync/processors/errorMaps.ts +++ b/packages/datastore/src/sync/processors/errorMaps.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorType } from '../../types'; -import { resolveServiceErrorStatusCode } from '../utils'; +import { ErrorType } from '~/src/types'; +import { resolveServiceErrorStatusCode } from '~/src/sync/utils'; export type ErrorMap = Partial<{ [key in ErrorType]: (error: Error) => boolean; @@ -16,6 +16,7 @@ export const mutationErrorMap: ErrorMap = { BadModel: () => false, BadRecord: error => { const { message } = error; + return ( /^Cannot return \w+ for [\w-_]+ type/.test(message) || /^Variable '.+' has coerced Null value for NonNull type/.test(message) @@ -34,10 +35,12 @@ export const subscriptionErrorMap: ErrorMap = { ConfigError: () => false, Transient: observableError => { const error = unwrapObservableError(observableError); + return connectionTimeout(error) || serverError(error); }, Unauthorized: observableError => { const error = unwrapObservableError(observableError); + return /Connection failed.+Unauthorized/.test(error.message); }, }; @@ -60,6 +63,8 @@ function unwrapObservableError(observableError: any) { const { errors: [error], } = ({ + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-empty-pattern errors: [], } = observableError); @@ -92,5 +97,6 @@ export function mapErrorToType(errorMap: ErrorMap, error: Error): ErrorType { return errorType; } } + return 'Unknown'; } diff --git a/packages/datastore/src/sync/processors/mutation.ts b/packages/datastore/src/sync/processors/mutation.ts index 6dba06d13c6..d65cdf16c6c 100644 --- a/packages/datastore/src/sync/processors/mutation.ts +++ b/packages/datastore/src/sync/processors/mutation.ts @@ -3,61 +3,60 @@ import { GraphQLResult } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; import { + BackgroundProcessManager, Category, CustomUserAgentDetails, DataStoreAction, - jitteredBackoff, + GraphQLAuthMode, NonRetryableError, + jitteredBackoff, retry, - BackgroundProcessManager, - GraphQLAuthMode, - AmplifyError, } from '@aws-amplify/core/internals/utils'; - import { Observable, Observer } from 'rxjs'; -import { MutationEvent } from '../'; -import { ModelInstanceCreator } from '../../datastore/datastore'; -import { ExclusiveStorage as Storage } from '../../storage/storage'; +import { MutationEvent } from '~/src/sync'; +import { ModelInstanceCreator } from '~/src/datastore/datastore'; +import { ExclusiveStorage as Storage } from '~/src/storage/storage'; import { + AmplifyContext, AuthModeStrategy, ConflictHandler, DISCARD, ErrorHandler, GraphQLCondition, InternalSchema, - isModelFieldType, - isTargetNameAssociation, ModelInstanceMetadata, OpType, PersistentModel, PersistentModelConstructor, + ProcessName, SchemaModel, TypeConstructorMap, - ProcessName, - AmplifyContext, -} from '../../types'; -import { extractTargetNamesFromSrc, USER, ID } from '../../util'; -import { MutationEventOutbox } from '../outbox'; + isModelFieldType, + isTargetNameAssociation, +} from '~/src/types'; +import { ID, USER, extractTargetNamesFromSrc } from '~/src/util'; +import { MutationEventOutbox } from '~/src/sync/outbox'; import { + TransformerMutationType, buildGraphQLOperation, createMutationInstanceFromModelOperation, getModelAuthModes, - TransformerMutationType, getTokenForCustomAuth, -} from '../utils'; -import { getMutationErrorType } from './errorMaps'; +} from '~/src/sync/utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { getMutationErrorType } from './errorMaps'; + const MAX_ATTEMPTS = 10; const logger = new ConsoleLogger('DataStore'); -type MutationProcessorEvent = { +interface MutationProcessorEvent { operation: TransformerMutationType; modelDefinition: SchemaModel; model: PersistentModel; hasMore: boolean; -}; +} class MutationProcessor { /** @@ -73,7 +72,8 @@ class MutationProcessor { SchemaModel, [TransformerMutationType, string, string][] >(); - private processing: boolean = false; + + private processing = false; private runningProcesses = new BackgroundProcessManager(); @@ -83,12 +83,12 @@ class MutationProcessor { private readonly userClasses: TypeConstructorMap, private readonly outbox: MutationEventOutbox, private readonly modelInstanceCreator: ModelInstanceCreator, - private readonly MutationEvent: PersistentModelConstructor, + private readonly MutationEventConstructor: PersistentModelConstructor, private readonly amplifyConfig: Record = {}, private readonly authModeStrategy: AuthModeStrategy, private readonly errorHandler: ErrorHandler, private readonly conflictHandler: ConflictHandler, - private readonly amplifyContext: AmplifyContext + private readonly amplifyContext: AmplifyContext, ) { this.amplifyContext.InternalAPI = this.amplifyContext.InternalAPI || InternalAPI; @@ -103,17 +103,17 @@ class MutationProcessor { const [createMutation] = buildGraphQLOperation( namespace, model, - 'CREATE' + 'CREATE', ); const [updateMutation] = buildGraphQLOperation( namespace, model, - 'UPDATE' + 'UPDATE', ); const [deleteMutation] = buildGraphQLOperation( namespace, model, - 'DELETE' + 'DELETE', ); this.typeQuery.set(model, [ @@ -207,7 +207,7 @@ class MutationProcessor { const authModeRetry = async () => { try { logger.debug( - `Attempting mutation with authMode: ${operationAuthModes[authModeAttempts]}` + `Attempting mutation with authMode: ${operationAuthModes[authModeAttempts]}`, ); const response = await this.jitteredRetry( namespaceName, @@ -216,14 +216,14 @@ class MutationProcessor { data, condition, modelConstructor, - this.MutationEvent, + this.MutationEventConstructor, head, operationAuthModes[authModeAttempts], - onTerminate + onTerminate, ); logger.debug( - `Mutation sent successfully with authMode: ${operationAuthModes[authModeAttempts]}` + `Mutation sent successfully with authMode: ${operationAuthModes[authModeAttempts]}`, ); return response; @@ -233,9 +233,11 @@ class MutationProcessor { logger.debug( `Mutation failed with authMode: ${ operationAuthModes[authModeAttempts - 1] - }` + }`, ); try { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -258,9 +260,10 @@ class MutationProcessor { operationAuthModes[authModeAttempts - 1] }. Retrying with authMode: ${ operationAuthModes[authModeAttempts] - }` + }`, ); - return await authModeRetry(); + + return authModeRetry(); } }; @@ -313,35 +316,35 @@ class MutationProcessor { data: string, condition: string, modelConstructor: PersistentModelConstructor, - MutationEvent: PersistentModelConstructor, + MutationEventConstructor: PersistentModelConstructor, mutationEvent: MutationEvent, authMode: GraphQLAuthMode, - onTerminate: Promise + onTerminate: Promise, ): Promise< [GraphQLResult>, string, SchemaModel] > { - return await retry( + return retry( async ( - model: string, - operation: TransformerMutationType, - data: string, - condition: string, - modelConstructor: PersistentModelConstructor, - MutationEvent: PersistentModelConstructor, - mutationEvent: MutationEvent + retryModel: string, + retryOperation: TransformerMutationType, + retryData: string, + retryCondition: string, + retryModelConstructor: PersistentModelConstructor, + retryMutationEventConstructor: PersistentModelConstructor, + retryMutationEvent: MutationEvent, ) => { const [query, variables, graphQLCondition, opName, modelDefinition] = this.createQueryVariables( namespaceName, - model, - operation, - data, - condition + retryModel, + retryOperation, + retryData, + retryCondition, ); const authToken = await getTokenForCustomAuth( authMode, - this.amplifyConfig + this.amplifyConfig, ); const tryWith = { @@ -352,7 +355,7 @@ class MutationProcessor { }; let attempt = 0; - const opType = this.opTypeFromTransformerOperation(operation); + const opType = this.opTypeFromTransformerOperation(retryOperation); const customUserAgentDetails: CustomUserAgentDetails = { category: Category.DataStore, @@ -361,13 +364,11 @@ class MutationProcessor { do { try { - const result = >>( - await this.amplifyContext.InternalAPI.graphql( - tryWith, - undefined, - customUserAgentDetails - ) - ); + const result = (await this.amplifyContext.InternalAPI.graphql( + tryWith, + undefined, + customUserAgentDetails, + )) as GraphQLResult>; // Use `as any` because TypeScript doesn't seem to like passing tuples // through generic params. @@ -402,20 +403,20 @@ class MutationProcessor { } else { try { retryWith = await this.conflictHandler!({ - modelConstructor, + modelConstructor: retryModelConstructor, localModel: this.modelInstanceCreator( - modelConstructor, - variables.input + retryModelConstructor, + variables.input, ), remoteModel: this.modelInstanceCreator( - modelConstructor, - error.data + retryModelConstructor, + error.data, ), operation: opType, attempts: attempt, }); - } catch (err) { - logger.warn('conflict trycatch', err); + } catch (_error) { + logger.warn('conflict trycatch', _error); continue; } } @@ -423,33 +424,32 @@ class MutationProcessor { if (retryWith === DISCARD) { // Query latest from server and notify merger - const [[, opName, query]] = buildGraphQLOperation( + const [[, retryOpName, retryQuery]] = buildGraphQLOperation( this.schema.namespaces[namespaceName], modelDefinition, - 'GET' + 'GET', ); - const authToken = await getTokenForCustomAuth( + const retryAuthToken = await getTokenForCustomAuth( authMode, - this.amplifyConfig + this.amplifyConfig, ); - const serverData = < - GraphQLResult> - >await this.amplifyContext.InternalAPI.graphql( - { - query, - variables: { id: variables.input.id }, - authMode, - authToken, - }, - undefined, - customUserAgentDetails - ); + const serverData = + (await this.amplifyContext.InternalAPI.graphql( + { + query: retryQuery, + variables: { id: variables.input.id }, + authMode, + authToken: retryAuthToken, + }, + undefined, + customUserAgentDetails, + )) as GraphQLResult>; // onTerminate cancel graphql() - return [serverData, opName, modelDefinition]; + return [serverData, retryOpName, modelDefinition]; } const namespace = this.schema.namespaces[namespaceName]; @@ -460,12 +460,12 @@ class MutationProcessor { namespace.relationships!, modelDefinition, opType, - modelConstructor, + retryModelConstructor, retryWith, graphQLCondition, - MutationEvent, + retryMutationEventConstructor, this.modelInstanceCreator, - mutationEvent.id + retryMutationEvent.id, ); await this.storage.save(updatedMutation); @@ -478,19 +478,24 @@ class MutationProcessor { 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', localModel: variables.input, message: error.message, - operation, + operation: retryOperation, errorType: getMutationErrorType(error), errorInfo: error.errorInfo, process: ProcessName.mutate, cause: error, remoteModel: error.data - ? this.modelInstanceCreator(modelConstructor, error.data) + ? this.modelInstanceCreator( + retryModelConstructor, + error.data, + ) : null!, }); - } catch (err) { - logger.warn('Mutation error handler failed with:', err); + } catch (_error) { + logger.warn('Mutation error handler failed with:', _error); } finally { // Return empty tuple, dequeues the mutation + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-unsafe-finally return error.data ? [ { data: { [opName]: error.data } }, @@ -506,6 +511,8 @@ class MutationProcessor { throw new NonRetryableError(err); } } + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-unmodified-loop-condition } while (tryWith); }, [ @@ -514,11 +521,11 @@ class MutationProcessor { data, condition, modelConstructor, - MutationEvent, + MutationEventConstructor, mutationEvent, ], safeJitteredBackoff, - onTerminate + onTerminate, ); } @@ -527,7 +534,7 @@ class MutationProcessor { model: string, operation: TransformerMutationType, data: string, - condition: string + condition: string, ): [string, Record, GraphQLCondition, string, SchemaModel] { const modelDefinition = this.schema.namespaces[namespaceName].models[model]; const { primaryKey } = this.schema.namespaces[namespaceName].keys![model]; @@ -540,10 +547,12 @@ class MutationProcessor { const queriesTuples = this.typeQuery.get(modelDefinition); const [, opName, query] = queriesTuples!.find( - ([transformerMutationType]) => transformerMutationType === operation + ([transformerMutationType]) => transformerMutationType === operation, )!; - const { _version, ...parsedData } = JSON.parse(data); + const { _version, ...parsedData } = JSON.parse( + data, + ) as ModelInstanceMetadata; // include all the fields that comprise a custom PK if one is specified const deleteInput = {}; @@ -552,14 +561,14 @@ class MutationProcessor { deleteInput[pkField] = parsedData[pkField]; } } else { - deleteInput[ID] = (parsedData).id; + deleteInput[ID] = (parsedData as any).id; } let mutationInput; if (operation === TransformerMutationType.DELETE) { // For DELETE mutations, only the key(s) are included in the input - mutationInput = deleteInput; + mutationInput = deleteInput as ModelInstanceMetadata; } else { // Otherwise, we construct the mutation input with the following logic mutationInput = {}; @@ -598,7 +607,7 @@ class MutationProcessor { // scalar fields / non-model types if (operation === TransformerMutationType.UPDATE) { - if (!parsedData.hasOwnProperty(name)) { + if (!Object.prototype.hasOwnProperty.call(parsedData, name)) { // for update mutations - strip out a field if it's unchanged continue; } @@ -615,7 +624,7 @@ class MutationProcessor { _version, }; - const graphQLCondition = JSON.parse(condition); + const graphQLCondition = JSON.parse(condition) as GraphQLCondition; const variables = { input, @@ -628,11 +637,12 @@ class MutationProcessor { : null, }), }; + return [query, variables, graphQLCondition, opName, modelDefinition]; } private opTypeFromTransformerOperation( - operation: TransformerMutationType + operation: TransformerMutationType, ): OpType { switch (operation) { case TransformerMutationType.CREATE: @@ -677,7 +687,7 @@ const originalJitteredBackoff = jitteredBackoff(MAX_RETRY_DELAY_MS); export const safeJitteredBackoff: typeof originalJitteredBackoff = ( attempt, _args, - error + error, ) => { const attemptResult = originalJitteredBackoff(attempt); diff --git a/packages/datastore/src/sync/processors/subscription.ts b/packages/datastore/src/sync/processors/subscription.ts index 36ddaeb8513..4be2f014108 100644 --- a/packages/datastore/src/sync/processors/subscription.ts +++ b/packages/datastore/src/sync/processors/subscription.ts @@ -3,54 +3,49 @@ import { GraphQLResult } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; import { + ConsoleLogger, Hub, HubCapsule, fetchAuthSession, - ConsoleLogger, } from '@aws-amplify/core'; import { + BackgroundProcessManager, Category, CustomUserAgentDetails, DataStoreAction, - BackgroundProcessManager, GraphQLAuthMode, - AmplifyError, JwtPayload, } from '@aws-amplify/core/internals/utils'; - import { Observable, Observer, SubscriptionLike } from 'rxjs'; import { + AmplifyContext, + AuthModeStrategy, + ErrorHandler, InternalSchema, + ModelPredicate, PersistentModel, - SchemaModel, - SchemaNamespace, PredicatesGroup, - ModelPredicate, - AuthModeStrategy, - ErrorHandler, ProcessName, - AmplifyContext, -} from '../../types'; + SchemaModel, + SchemaNamespace, +} from '~/src/types'; import { + RTFError, + TransformerMutationType, buildSubscriptionGraphQLOperation, + generateRTFRemediation, getAuthorizationRules, getModelAuthModes, - getUserGroupsFromToken, - TransformerMutationType, getTokenForCustomAuth, + getUserGroupsFromToken, predicateToGraphQLFilter, - dynamicAuthFields, - filterFields, - repeatedFieldInGroup, - countFilterCombinations, - RTFError, - generateRTFRemediation, -} from '../utils'; -import { ModelPredicateCreator } from '../../predicates'; -import { validatePredicate } from '../../util'; -import { getSubscriptionErrorType } from './errorMaps'; +} from '~/src/sync/utils'; +import { ModelPredicateCreator } from '~/src/predicates'; +import { validatePredicate } from '~/src/util'; import { CONTROL_MSG as PUBSUB_CONTROL_MSG } from '@aws-amplify/api-graphql'; +import { getSubscriptionErrorType } from './errorMaps'; + const logger = new ConsoleLogger('DataStore'); export enum CONTROL_MSG { @@ -63,20 +58,22 @@ export enum USER_CREDENTIALS { 'auth', } -type AuthorizationInfo = { +interface AuthorizationInfo { authMode: GraphQLAuthMode; isOwner: boolean; ownerField?: string; ownerValue?: string; -}; +} class SubscriptionProcessor { private readonly typeQuery = new WeakMap< SchemaModel, [TransformerMutationType, string, string][] >(); + private buffer: [TransformerMutationType, SchemaModel, PersistentModel][] = []; + private dataObserver!: Observer; private runningProcesses = new BackgroundProcessManager(); @@ -92,7 +89,7 @@ class SubscriptionProcessor { private readonly errorHandler: ErrorHandler, private readonly amplifyContext: AmplifyContext = { InternalAPI, - } + }, ) {} private buildSubscription( @@ -102,7 +99,7 @@ class SubscriptionProcessor { userCredentials: USER_CREDENTIALS, oidcTokenPayload: JwtPayload | undefined, authMode: GraphQLAuthMode, - filterArg: boolean = false + filterArg = false, ): { opType: TransformerMutationType; opName: string; @@ -119,7 +116,7 @@ class SubscriptionProcessor { userCredentials, aws_appsync_authenticationType, oidcTokenPayload, - authMode + authMode, ) || {}; const [opType, opName, query] = buildSubscriptionGraphQLOperation( @@ -128,8 +125,9 @@ class SubscriptionProcessor { transformerMutationType, isOwner, ownerField!, - filterArg + filterArg, ); + return { authMode, opType, opName, query, isOwner, ownerField, ownerValue }; } @@ -138,14 +136,14 @@ class SubscriptionProcessor { userCredentials: USER_CREDENTIALS, defaultAuthType: GraphQLAuthMode, oidcTokenPayload: JwtPayload | undefined, - authMode: GraphQLAuthMode + authMode: GraphQLAuthMode, ): AuthorizationInfo { const rules = getAuthorizationRules(model); // Return null if user doesn't have proper credentials for private API with IAM auth const iamPrivateAuth = authMode === 'iam' && rules.find( - rule => rule.authStrategy === 'private' && rule.provider === 'iam' + rule => rule.authStrategy === 'private' && rule.provider === 'iam', ); if (iamPrivateAuth && userCredentials === USER_CREDENTIALS.unauth) { @@ -159,17 +157,19 @@ class SubscriptionProcessor { const groupAuthRules = rules.filter( rule => rule.authStrategy === 'groups' && - ['userPools', 'oidc'].includes(rule.provider) + ['userPools', 'oidc'].includes(rule.provider), ); const validGroup = (authMode === 'oidc' || authMode === 'userPool') && + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line array-callback-return groupAuthRules.find(groupAuthRule => { // validate token against groupClaim if (oidcTokenPayload) { const oidcUserGroups = getUserGroupsFromToken( oidcTokenPayload, - groupAuthRule + groupAuthRule, ); return [...oidcUserGroups].find(userGroup => { @@ -200,7 +200,7 @@ class SubscriptionProcessor { ? rules.filter( rule => rule.authStrategy === 'owner' && - (rule.provider === 'oidc' || rule.provider === 'userPools') + (rule.provider === 'oidc' || rule.provider === 'userPools'), ) : []; @@ -233,8 +233,8 @@ class SubscriptionProcessor { } private hubQueryCompletionListener( - completed: Function, - capsule: HubCapsule<'datastore', { event: string }> + completed: (...args: any[]) => unknown, + capsule: HubCapsule<'datastore', { event: string }>, ) { const { payload: { event }, @@ -257,13 +257,14 @@ class SubscriptionProcessor { // Creating subs for each model/operation combo so they can be unsubscribed // independently, since the auth retry behavior is asynchronous. - let subscriptions: { - [modelName: string]: { + let subscriptions: Record< + string, + { [TransformerMutationType.CREATE]: SubscriptionLike[]; [TransformerMutationType.UPDATE]: SubscriptionLike[]; [TransformerMutationType.DELETE]: SubscriptionLike[]; - }; - } = {}; + } + > = {}; let oidcTokenPayload: JwtPayload | undefined; let userCredentials = USER_CREDENTIALS.none; this.runningProcesses.add(async () => { @@ -326,7 +327,7 @@ class SubscriptionProcessor { const predicatesGroup = ModelPredicateCreator.getPredicates( this.syncPredicates.get(modelDefinition)!, - false + false, ); const addFilterArg = predicatesGroup !== undefined; @@ -336,7 +337,7 @@ class SubscriptionProcessor { // 2. RTF error - retry without sending filter arg. (filtering will fall back to clientside) const subscriptionRetry = async ( operation, - addFilter = addFilterArg + addFilter = addFilterArg, ) => { const { opType: transformerMutationType, @@ -353,12 +354,12 @@ class SubscriptionProcessor { userCredentials, oidcTokenPayload, readAuthModes[operationAuthModeAttempts[operation]], - addFilter + addFilter, ); const authToken = await getTokenForCustomAuth( authMode, - this.amplifyConfig + this.amplifyConfig, ); const variables = {}; @@ -369,15 +370,17 @@ class SubscriptionProcessor { }; if (addFilter && predicatesGroup) { - variables['filter'] = + // TODO(eslint): remove this linter suppression with refactoring. + (variables as any).filter = predicateToGraphQLFilter(predicatesGroup); } if (isOwner) { if (!ownerValue) { observer.error( - 'Owner field required, sign in is needed in order to perform this operation' + 'Owner field required, sign in is needed in order to perform this operation', ); + return; } @@ -387,21 +390,22 @@ class SubscriptionProcessor { logger.debug( `Attempting ${operation} subscription with authMode: ${ readAuthModes[operationAuthModeAttempts[operation]] - }` + }`, ); - const queryObservable = < - Observable>> - >(this.amplifyContext.InternalAPI.graphql( - { - query, - variables, - ...{ authMode }, - authToken, - }, - undefined, - customUserAgentDetails - )); + const queryObservable = + this.amplifyContext.InternalAPI.graphql( + { + query, + variables, + ...{ authMode }, + authToken, + }, + undefined, + customUserAgentDetails, + ) as unknown as Observable< + GraphQLResult> + >; let subscriptionReadyCallback: (param?: unknown) => void; @@ -414,29 +418,29 @@ class SubscriptionProcessor { next: result => { const { data, errors } = result; if (Array.isArray(errors) && errors.length > 0) { - const messages = (< - { + const messages = ( + errors as { message: string; }[] - >errors).map(({ message }) => message); + ).map(({ message }) => message); logger.warn( `Skipping incoming subscription. Messages: ${messages.join( - '\n' - )}` + '\n', + )}`, ); this.drainBuffer(); + return; } - const predicatesGroup = + const syncPredicatesGroup = ModelPredicateCreator.getPredicates( this.syncPredicates.get(modelDefinition)!, - false + false, ); - // @ts-ignore const { [opName]: record } = data; // checking incoming subscription against syncPredicate. @@ -446,13 +450,13 @@ class SubscriptionProcessor { if ( this.passesPredicateValidation( record, - predicatesGroup! + syncPredicatesGroup!, ) ) { this.pushToBuffer( transformerMutationType, modelDefinition, - record + record, ); } this.drainBuffer(); @@ -461,6 +465,8 @@ class SubscriptionProcessor { const { errors: [{ message = '' } = {}], } = ({ + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-empty-pattern errors: [], } = subscriptionError); @@ -470,7 +476,7 @@ class SubscriptionProcessor { this.catchRTFError( message, modelDefinition, - predicatesGroup + predicatesGroup, ); // Catch RTF errors @@ -479,7 +485,7 @@ class SubscriptionProcessor { subscriptions[modelDefinition.name][ transformerMutationType ].forEach(subscription => - subscription.unsubscribe() + subscription.unsubscribe(), ); subscriptions[modelDefinition.name][ @@ -488,22 +494,23 @@ class SubscriptionProcessor { // retry subscription connection without filter subscriptionRetry(operation, false); + return; } if ( message.includes( - PUBSUB_CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR + PUBSUB_CONTROL_MSG.REALTIME_SUBSCRIPTION_INIT_ERROR, ) || message.includes( - PUBSUB_CONTROL_MSG.CONNECTION_FAILED + PUBSUB_CONTROL_MSG.CONNECTION_FAILED, ) ) { // Unsubscribe and clear subscription array for model/operation subscriptions[modelDefinition.name][ transformerMutationType ].forEach(subscription => - subscription.unsubscribe() + subscription.unsubscribe(), ); subscriptions[modelDefinition.name][ transformerMutationType @@ -520,7 +527,7 @@ class SubscriptionProcessor { readAuthModes[ operationAuthModeAttempts[operation] - 1 ] - }` + }`, ); } else { // retry with different auth mode. Do not trigger @@ -534,9 +541,10 @@ class SubscriptionProcessor { readAuthModes[ operationAuthModeAttempts[operation] ] - }` + }`, ); subscriptionRetry(operation); + return; } } @@ -544,6 +552,8 @@ class SubscriptionProcessor { logger.warn('subscriptionError', message); try { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -560,7 +570,7 @@ class SubscriptionProcessor { } catch (e) { logger.error( 'Subscription error handler failed with:', - e + e, ); } @@ -576,31 +586,31 @@ class SubscriptionProcessor { } observer.error(message); }, - }) + }), ); promises.push( (async () => { let boundFunction: any; let removeBoundFunctionListener: () => void; - await new Promise(res => { - subscriptionReadyCallback = res; + await new Promise(resolve => { + subscriptionReadyCallback = resolve; boundFunction = this.hubQueryCompletionListener.bind( this, - res + resolve, ); removeBoundFunctionListener = Hub.listen( 'api', - boundFunction + boundFunction, ); }); removeBoundFunctionListener(); - })() + })(), ); }; operations.forEach(op => subscriptionRetry(op)); - }) + }), ); }); @@ -608,20 +618,26 @@ class SubscriptionProcessor { this.runningProcesses.add(() => Promise.all(promises).then(() => { observer.next(CONTROL_MSG.CONNECTED); - }) + }), ); }, 'subscription processor new subscriber'); return this.runningProcesses.addCleaner(async () => { Object.keys(subscriptions).forEach(modelName => { subscriptions[modelName][TransformerMutationType.CREATE].forEach( - subscription => subscription.unsubscribe() + subscription => { + subscription.unsubscribe(); + }, ); subscriptions[modelName][TransformerMutationType.UPDATE].forEach( - subscription => subscription.unsubscribe() + subscription => { + subscription.unsubscribe(); + }, ); subscriptions[modelName][TransformerMutationType.DELETE].forEach( - subscription => subscription.unsubscribe() + subscription => { + subscription.unsubscribe(); + }, ); }); }); @@ -648,7 +664,7 @@ class SubscriptionProcessor { private passesPredicateValidation( record: PersistentModel, - predicatesGroup: PredicatesGroup + predicatesGroup: PredicatesGroup, ): boolean { if (!predicatesGroup) { return true; @@ -662,14 +678,16 @@ class SubscriptionProcessor { private pushToBuffer( transformerMutationType: TransformerMutationType, modelDefinition: SchemaModel, - data: PersistentModel + data: PersistentModel, ) { this.buffer.push([transformerMutationType, modelDefinition, data]); } private drainBuffer() { if (this.dataObserver) { - this.buffer.forEach(data => this.dataObserver.next!(data)); + this.buffer.forEach(data => { + this.dataObserver.next!(data); + }); this.buffer = []; } } @@ -682,7 +700,7 @@ class SubscriptionProcessor { private catchRTFError( message: string, modelDefinition: SchemaModel, - predicatesGroup: PredicatesGroup | undefined + predicatesGroup: PredicatesGroup | undefined, ): boolean { const header = 'Backend subscriptions filtering error.\n' + @@ -700,17 +718,18 @@ class SubscriptionProcessor { const [_errorMsg, errorType] = Object.entries(messageErrorTypeMap).find(([errorMsg]) => - message.includes(errorMsg) + message.includes(errorMsg), ) || []; if (errorType !== undefined) { const remediationMessage = generateRTFRemediation( errorType, modelDefinition, - predicatesGroup + predicatesGroup, ); logger.warn(`${header}\n${message}\n${remediationMessage}`); + return true; } diff --git a/packages/datastore/src/sync/processors/sync.ts b/packages/datastore/src/sync/processors/sync.ts index 40d07913e8f..f54fa1d6ff9 100644 --- a/packages/datastore/src/sync/processors/sync.ts +++ b/packages/datastore/src/sync/processors/sync.ts @@ -4,40 +4,39 @@ import { GraphQLResult } from '@aws-amplify/api'; import { InternalAPI } from '@aws-amplify/api/internals'; import { Observable } from 'rxjs'; import { + AmplifyContext, + AuthModeStrategy, + ErrorHandler, + GraphQLFilter, InternalSchema, ModelInstanceMetadata, - SchemaModel, ModelPredicate, PredicatesGroup, - GraphQLFilter, - AuthModeStrategy, - ErrorHandler, ProcessName, - AmplifyContext, -} from '../../types'; + SchemaModel, +} from '~/src/types'; import { buildGraphQLOperation, - getModelAuthModes, getClientSideAuthError, getForbiddenError, - predicateToGraphQLFilter, + getModelAuthModes, getTokenForCustomAuth, -} from '../utils'; + predicateToGraphQLFilter, +} from '~/src/sync/utils'; import { - jitteredExponentialRetry, + BackgroundProcessManager, Category, CustomUserAgentDetails, DataStoreAction, - NonRetryableError, - BackgroundProcessManager, GraphQLAuthMode, - AmplifyError, + NonRetryableError, + jitteredExponentialRetry, } from '@aws-amplify/core/internals/utils'; +import { ConsoleLogger, Hub } from '@aws-amplify/core'; +import { ModelPredicateCreator } from '~/src/predicates'; -import { Amplify, ConsoleLogger, Hub } from '@aws-amplify/core'; - -import { ModelPredicateCreator } from '../../predicates'; import { getSyncErrorType } from './errorMaps'; + const opResultDefaults = { items: [], nextToken: null, @@ -60,7 +59,7 @@ class SyncProcessor { private readonly amplifyConfig: Record = {}, private readonly authModeStrategy: AuthModeStrategy, private readonly errorHandler: ErrorHandler, - private readonly amplifyContext: AmplifyContext + private readonly amplifyContext: AmplifyContext, ) { amplifyContext.InternalAPI = amplifyContext.InternalAPI || InternalAPI; this.generateQueries(); @@ -74,7 +73,7 @@ class SyncProcessor { const [[, ...opNameQuery]] = buildGraphQLOperation( namespace, model, - 'LIST' + 'LIST', ); this.typeQuery.set(model, opNameQuery); @@ -89,7 +88,7 @@ class SyncProcessor { const predicatesGroup: PredicatesGroup = ModelPredicateCreator.getPredicates( this.syncPredicates.get(model)!, - false + false, )!; if (!predicatesGroup) { @@ -105,7 +104,7 @@ class SyncProcessor { nextToken: string, limit: number = null!, filter: GraphQLFilter, - onTerminate: Promise + onTerminate: Promise, ): Promise<{ nextToken: string; startedAt: number; items: T[] }> { const [opName, query] = this.typeQuery.get(modelDefinition)!; @@ -130,13 +129,13 @@ class SyncProcessor { const authModeRetry = async () => { if (!this.runningProcesses.isOpen) { throw new Error( - 'sync.retreievePage termination was requested. Exiting.' + 'sync.retreievePage termination was requested. Exiting.', ); } try { logger.debug( - `Attempting sync with authMode: ${readAuthModes[authModeAttempts]}` + `Attempting sync with authMode: ${readAuthModes[authModeAttempts]}`, ); const response = await this.jitteredRetry({ query, @@ -147,8 +146,9 @@ class SyncProcessor { onTerminate, }); logger.debug( - `Sync successful with authMode: ${readAuthModes[authModeAttempts]}` + `Sync successful with authMode: ${readAuthModes[authModeAttempts]}`, ); + return response; } catch (error) { authModeAttempts++; @@ -158,7 +158,7 @@ class SyncProcessor { if (getClientSideAuthError(error) || getForbiddenError(error)) { // return empty list of data so DataStore will continue to sync other models logger.warn( - `User is unauthorized to query ${opName} with auth mode ${authMode}. No data could be returned.` + `User is unauthorized to query ${opName} with auth mode ${authMode}. No data could be returned.`, ); return { @@ -172,9 +172,10 @@ class SyncProcessor { logger.debug( `Sync failed with authMode: ${ readAuthModes[authModeAttempts - 1] - }. Retrying with authMode: ${readAuthModes[authModeAttempts]}` + }. Retrying with authMode: ${readAuthModes[authModeAttempts]}`, ); - return await authModeRetry(); + + return authModeRetry(); } }; @@ -206,20 +207,23 @@ class SyncProcessor { authMode: GraphQLAuthMode; onTerminate: Promise; }): Promise< - GraphQLResult<{ - [opName: string]: { - items: T[]; - nextToken: string; - startedAt: number; - }; - }> + GraphQLResult< + Record< + string, + { + items: T[]; + nextToken: string; + startedAt: number; + } + > + > > { - return await jitteredExponentialRetry( - async (query, variables) => { + return jitteredExponentialRetry( + async (retryQuery, retryVariables) => { try { const authToken = await getTokenForCustomAuth( authMode, - this.amplifyConfig + this.amplifyConfig, ); const customUserAgentDetails: CustomUserAgentDetails = { @@ -229,13 +233,13 @@ class SyncProcessor { return await this.amplifyContext.InternalAPI.graphql( { - query, - variables, + query: retryQuery, + variables: retryVariables, authMode, authToken, }, undefined, - customUserAgentDetails + customUserAgentDetails, ); // TODO: onTerminate.then(() => API.cancel(...)) @@ -254,20 +258,20 @@ class SyncProcessor { const unauthorized = error?.errors && (error.errors as [any]).some( - err => err.errorType === 'Unauthorized' + err => err.errorType === 'Unauthorized', ); const otherErrors = error?.errors && (error.errors as [any]).filter( - err => err.errorType !== 'Unauthorized' + err => err.errorType !== 'Unauthorized', ); const result = error; if (hasItems) { result.data[opName].items = result.data[opName].items.filter( - item => item !== null + item => item !== null, ); } @@ -275,6 +279,8 @@ class SyncProcessor { await Promise.all( otherErrors.map(async err => { try { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -290,7 +296,7 @@ class SyncProcessor { } catch (e) { logger.error('Sync error handler failed with:', e); } - }) + }), ); Hub.dispatch('datastore', { event: 'nonApplicableDataReceived', @@ -350,12 +356,12 @@ class SyncProcessor { }, [query, variables], undefined, - onTerminate + onTerminate, ); } start( - typesLastSync: Map + typesLastSync: Map, ): Observable { const { maxRecordsToSync, syncPageSize } = this.amplifyConfig; const parentPromises = new Map>(); @@ -363,14 +369,15 @@ class SyncProcessor { const sortedTypesLastSyncs = Object.values(this.schema.namespaces).reduce( (map, namespace) => { for (const modelName of Array.from( - namespace.modelTopologicalOrdering!.keys() + namespace.modelTopologicalOrdering!.keys(), )) { const typeLastSync = typesLastSync.get(namespace.models[modelName]); map.set(namespace.models[modelName], typeLastSync!); } + return map; }, - new Map() + new Map(), ); const allModelsReady = Array.from(sortedTypesLastSyncs.entries()) @@ -391,10 +398,11 @@ class SyncProcessor { namespace ].modelTopologicalOrdering!.get(modelDefinition.name); const promises = parents!.map(parent => - parentPromises.get(`${namespace}_${parent}`) + parentPromises.get(`${namespace}_${parent}`), ); - - const promise = new Promise(async res => { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-async-promise-executor + const promise = new Promise(async resolve => { await Promise.all(promises); do { @@ -405,14 +413,17 @@ class SyncProcessor { */ if (!this.runningProcesses.isOpen) { logger.debug( - `Sync processor has been stopped, terminating sync for ${modelDefinition.name}` + `Sync processor has been stopped, terminating sync for ${modelDefinition.name}`, ); - return res(); + + resolve(); + + return; } const limit = Math.min( maxRecordsToSync - recordsReceived, - syncPageSize + syncPageSize, ); /** @@ -427,10 +438,12 @@ class SyncProcessor { nextToken, limit, filter, - onTerminate + onTerminate, )); } catch (error) { try { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await this.errorHandler({ recoverySuggestion: 'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues', @@ -472,16 +485,16 @@ class SyncProcessor { }); } while (!done); - res(); + resolve(); }); parentPromises.set( `${namespace}_${modelDefinition.name}`, - promise + promise, ); await promise; - }, `adding model ${modelDefinition.name}`) + }, `adding model ${modelDefinition.name}`), ); Promise.all(allModelsReady as Promise[]).then(() => { @@ -500,13 +513,13 @@ class SyncProcessor { } } -export type SyncModelPage = { +export interface SyncModelPage { namespace: string; modelDefinition: SchemaModel; items: ModelInstanceMetadata[]; startedAt: number; done: boolean; isFullSync: boolean; -}; +} export { SyncProcessor }; diff --git a/packages/datastore/src/sync/utils.ts b/packages/datastore/src/sync/utils.ts index bc1c42f99ea..2d4a4aa1b56 100644 --- a/packages/datastore/src/sync/utils.ts +++ b/packages/datastore/src/sync/utils.ts @@ -1,53 +1,61 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { GraphQLAuthError } from '@aws-amplify/api'; -import type { GraphQLError } from 'graphql'; import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils'; -import { ModelInstanceCreator } from '../datastore/datastore'; +import { ModelInstanceCreator } from '~/src/datastore/datastore'; import { + AuthModeStrategy, AuthorizationRule, GraphQLCondition, - GraphQLFilter, GraphQLField, - isEnumFieldType, - isGraphQLScalarType, - isPredicateObj, - isSchemaModel, - isSchemaModelWithAttributes, - isTargetNameAssociation, - isNonModelFieldType, + GraphQLFilter, + InternalSchema, + ModelAttributes, ModelFields, ModelInstanceMetadata, + ModelOperation, OpType, PersistentModel, PersistentModelConstructor, - PredicatesGroup, PredicateObject, + PredicatesGroup, RelationshipType, SchemaModel, SchemaNamespace, SchemaNonModel, - ModelOperation, - InternalSchema, - AuthModeStrategy, - ModelAttributes, + isEnumFieldType, + isGraphQLScalarType, + isNonModelFieldType, isPredicateGroup, -} from '../types'; + isPredicateObj, + isSchemaModel, + isSchemaModelWithAttributes, + isTargetNameAssociation, +} from '~/src/types'; import { - extractPrimaryKeyFieldNames, - establishRelationAndKeys, IDENTIFIER_KEY_SEPARATOR, -} from '../util'; -import { MutationEvent } from './'; + establishRelationAndKeys, + extractPrimaryKeyFieldNames, +} from '~/src/util'; import { ConsoleLogger } from '@aws-amplify/core'; +import { MutationEvent } from './'; + +import type { GraphQLError } from 'graphql'; + const logger = new ConsoleLogger('DataStore'); enum GraphQLOperationType { LIST = 'query', CREATE = 'mutation', + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values UPDATE = 'mutation', + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values DELETE = 'mutation', + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values GET = 'query', } @@ -64,22 +72,22 @@ const dummyMetadata: ModelInstanceMetadata = { _deleted: undefined!, }; -const metadataFields = <(keyof ModelInstanceMetadata)[]>( - Object.keys(dummyMetadata) -); -export function getMetadataFields(): ReadonlyArray { +const metadataFields = Object.keys( + dummyMetadata, +) as (keyof ModelInstanceMetadata)[]; +export function getMetadataFields(): readonly string[] { return metadataFields; } export function generateSelectionSet( namespace: SchemaNamespace, - modelDefinition: SchemaModel | SchemaNonModel + modelDefinition: SchemaModel | SchemaNonModel, ): string { const scalarFields = getScalarFields(modelDefinition); const nonModelFields = getNonModelFields(namespace, modelDefinition); const implicitOwnerField = getImplicitOwnerField( modelDefinition, - scalarFields + scalarFields, ); let scalarAndMetadataFields = Object.values(scalarFields) @@ -100,35 +108,39 @@ export function generateSelectionSet( function getImplicitOwnerField( modelDefinition: SchemaModel | SchemaNonModel, - scalarFields: ModelFields + scalarFields: ModelFields, ) { const ownerFields = getOwnerFields(modelDefinition); if (!scalarFields.owner && ownerFields.includes('owner')) { return ['owner']; } + return []; } function getOwnerFields( - modelDefinition: SchemaModel | SchemaNonModel + modelDefinition: SchemaModel | SchemaNonModel, ): string[] { const ownerFields: string[] = []; if (isSchemaModelWithAttributes(modelDefinition)) { modelDefinition.attributes!.forEach(attr => { if (attr.properties && attr.properties.rules) { - const rule = attr.properties.rules.find(rule => rule.allow === 'owner'); + const rule = attr.properties.rules.find( + propertyRule => propertyRule.allow === 'owner', + ); if (rule && rule.ownerField) { ownerFields.push(rule.ownerField); } } }); } + return ownerFields; } function getScalarFields( - modelDefinition: SchemaModel | SchemaNonModel + modelDefinition: SchemaModel | SchemaNonModel, ): ModelFields { const { fields } = modelDefinition; @@ -152,7 +164,7 @@ function getScalarFields( // Used for generating the selection set for queries and mutations function getConnectionFields( modelDefinition: SchemaModel, - namespace: SchemaNamespace + namespace: SchemaNamespace, ): string[] { const result: string[] = []; @@ -174,10 +186,12 @@ function getConnectionFields( const [relations] = establishRelationAndKeys(namespace); const connectedModelName = + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line dot-notation modelDefinition.fields[name].type['model']; const byPkIndex = relations[connectedModelName].indexes.find( - ([name]) => name === 'byPk' + ([indexName]) => indexName === 'byPk', ); const keyFields = byPkIndex && byPkIndex[1]; const keyFieldSelectionSet = keyFields?.join(' '); @@ -200,7 +214,7 @@ function getConnectionFields( function getNonModelFields( namespace: SchemaNamespace, - modelDefinition: SchemaModel | SchemaNonModel + modelDefinition: SchemaModel | SchemaNonModel, ): string[] { const result: string[] = []; @@ -208,17 +222,21 @@ function getNonModelFields( if (isNonModelFieldType(type)) { const typeDefinition = namespace.nonModels![type.nonModel]; const scalarFields = Object.values(getScalarFields(typeDefinition)).map( - ({ name }) => name + ({ name: fieldName }) => fieldName, ); const nested: string[] = []; Object.values(typeDefinition.fields).forEach(field => { - const { type, name } = field; + const { type: fieldType, name: fieldName } = field; - if (isNonModelFieldType(type)) { - const typeDefinition = namespace.nonModels![type.nonModel]; + if (isNonModelFieldType(fieldType)) { + const nonModelTypeDefinition = + namespace.nonModels![fieldType.nonModel]; nested.push( - `${name} { ${generateSelectionSet(namespace, typeDefinition)} }` + `${fieldName} { ${generateSelectionSet( + namespace, + nonModelTypeDefinition, + )} }`, ); } }); @@ -231,7 +249,7 @@ function getNonModelFields( } export function getAuthorizationRules( - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): AuthorizationRule[] { // Searching for owner authorization on attributes const authConfig = ([] as ModelAttributes) @@ -293,6 +311,7 @@ export function getAuthorizationRules( if (isOwnerAuth) { // owner rules has least priority resultRules.push(authRule); + return; } @@ -308,7 +327,7 @@ export function buildSubscriptionGraphQLOperation( transformerMutationType: TransformerMutationType, isOwnerAuthorization: boolean, ownerField: string, - filterArg: boolean = false + filterArg = false, ): [TransformerMutationType, string, string] { const selectionSet = generateSelectionSet(namespace, modelDefinition); @@ -346,7 +365,7 @@ export function buildSubscriptionGraphQLOperation( export function buildGraphQLOperation( namespace: SchemaNamespace, modelDefinition: SchemaModel, - graphQLOpType: keyof typeof GraphQLOperationType + graphQLOpType: keyof typeof GraphQLOperationType, ): [TransformerMutationType, string, string][] { let selectionSet = generateSelectionSet(namespace, modelDefinition); @@ -421,7 +440,7 @@ export function createMutationInstanceFromModelOperation< condition: GraphQLCondition, MutationEventConstructor: PersistentModelConstructor, modelInstanceCreator: ModelInstanceCreator, - id?: string + id?: string, ): MutationEvent { let operation: TransformerMutationType; @@ -453,6 +472,7 @@ export function createMutationInstanceFromModelOperation< if (isAWSJSON) { return JSON.stringify(v); } + return v; }; @@ -473,7 +493,7 @@ export function createMutationInstanceFromModelOperation< export function predicateToGraphQLCondition( predicate: PredicatesGroup, - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): GraphQLCondition { const result = {}; @@ -491,12 +511,13 @@ export function predicateToGraphQLCondition( // key fields from the predicate/condition when ALL of the keyFields are present and using `eq` operators const keyFields = extractPrimaryKeyFieldNames(modelDefinition); + return predicateToGraphQLFilter(predicate, keyFields) as GraphQLCondition; } /** * @param predicatesGroup - Predicate Group @returns GQL Filter Expression from Predicate Group - + @remarks Flattens redundant list predicates @example @@ -511,7 +532,7 @@ export function predicateToGraphQLCondition( export function predicateToGraphQLFilter( predicatesGroup: PredicatesGroup, fieldsToOmit: string[] = [], - root = true + root = true, ): GraphQLFilter { const result: GraphQLFilter = {}; @@ -537,6 +558,7 @@ export function predicateToGraphQLFilter( }; children.push(gqlField); + return; } @@ -557,6 +579,7 @@ export function predicateToGraphQLFilter( ) { delete result[type]; Object.assign(result, child); + return result; } } @@ -674,7 +697,7 @@ export function countFilterCombinations(group?: PredicatesGroup): number { * ``` */ export function repeatedFieldInGroup( - group?: PredicatesGroup + group?: PredicatesGroup, ): string | null { if (!group || !Array.isArray(group.predicates)) return null; @@ -693,6 +716,7 @@ export function repeatedFieldInGroup( } seen[fieldName] = true; } + return null; }; @@ -708,12 +732,12 @@ export function repeatedFieldInGroup( // field value will be single object const predicateObjects = values.filter( - v => !Array.isArray(Object.values(v)[0]) + v => !Array.isArray(Object.values(v)[0]), ); // group value will be an array const predicateGroups = values.filter(v => - Array.isArray(Object.values(v)[0]) + Array.isArray(Object.values(v)[0]), ); if (key === 'and') { @@ -741,7 +765,7 @@ export enum RTFError { export function generateRTFRemediation( errorType: RTFError, modelDefinition: SchemaModel, - predicatesGroup: PredicatesGroup | undefined + predicatesGroup: PredicatesGroup | undefined, ): string { const selSyncFields = filterFields(predicatesGroup); const selSyncFieldStr = [...selSyncFields].join(', '); @@ -779,6 +803,7 @@ export function generateRTFRemediation( `Dynamic auth modes, such as owner auth and dynamic group auth factor in to the number of combinations you're using.\n` + `You currently have ${dynamicAuthModeFields.size} dynamic auth mode(s) configured on this model: ${dynamicAuthFieldsStr}.`; } + return message; } @@ -796,8 +821,8 @@ export function generateRTFRemediation( } export function getUserGroupsFromToken( - token: { [field: string]: any }, - rule: AuthorizationRule + token: Record, + rule: AuthorizationRule, ): string[] { // validate token against groupClaim let userGroups: string[] | string = token[rule.groupClaim] || []; @@ -856,11 +881,12 @@ export async function getModelAuthModes({ // Use default auth mode if nothing is returned from authModeStrategy modelAuthModes[operation] = [defaultAuthMode]; } - }) + }), ); } catch (error) { logger.debug(`Error getting auth modes for model: ${modelName}`, error); } + return modelAuthModes; } @@ -869,7 +895,7 @@ export function getForbiddenError(error) { let forbiddenError; if (error && error.errors) { forbiddenError = (error.errors as [any]).find(err => - forbiddenErrorCodes.includes(resolveServiceErrorStatusCode(err)) + forbiddenErrorCodes.includes(resolveServiceErrorStatusCode(err)), ); } else if (error && error.message) { forbiddenError = error; @@ -879,19 +905,24 @@ export function getForbiddenError(error) { return ( forbiddenError.message ?? `Request failed with status code ${resolveServiceErrorStatusCode( - forbiddenError + forbiddenError, )}` ); } + return null; } export function resolveServiceErrorStatusCode(error: unknown): number | null { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line dot-notation if (error?.['$metadata']?.['httpStatusCode']) { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line dot-notation return Number(error?.['$metadata']?.['httpStatusCode']); } else if ((error as GraphQLError)?.originalError) { return resolveServiceErrorStatusCode( - (error as GraphQLError)?.originalError + (error as GraphQLError)?.originalError, ); } else { return null; @@ -904,14 +935,15 @@ export function getClientSideAuthError(error) { error && error.message && clientSideAuthErrors.find(clientError => - error.message.includes(clientError) + error.message.includes(clientError), ); + return clientSideError || null; } export async function getTokenForCustomAuth( authMode: GraphQLAuthMode, - amplifyConfig: Record = {} + amplifyConfig: Record = {}, ): Promise { if (authMode === 'lambda') { const { @@ -920,16 +952,17 @@ export async function getTokenForCustomAuth( if (functionAuthProvider && typeof functionAuthProvider === 'function') { try { const { token } = await functionAuthProvider(); + return token; } catch (error) { throw new Error( - `Error retrieving token from \`functionAuthProvider\`: ${error}` + `Error retrieving token from \`functionAuthProvider\`: ${error}`, ); } } else { // TODO: add docs link once available throw new Error( - 'You must provide a `functionAuthProvider` function to `DataStore.configure` when using lambda' + 'You must provide a `functionAuthProvider` function to `DataStore.configure` when using lambda', ); } } @@ -938,7 +971,7 @@ export async function getTokenForCustomAuth( // Util that takes a modelDefinition and model and returns either the id value(s) or the custom primary key value(s) export function getIdentifierValue( modelDefinition: SchemaModel, - model: ModelInstanceMetadata | PersistentModel + model: ModelInstanceMetadata | PersistentModel, ): string { const pkFieldNames = extractPrimaryKeyFieldNames(modelDefinition); diff --git a/packages/datastore/src/types.ts b/packages/datastore/src/types.ts index a39c83864de..6721a880d0d 100644 --- a/packages/datastore/src/types.ts +++ b/packages/datastore/src/types.ts @@ -1,50 +1,51 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { InternalAPI } from '@aws-amplify/api/internals'; +import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils'; + import { ModelInstanceCreator } from './datastore/datastore'; import { + NAMESPACES, + extractPrimaryKeyFieldNames, isAWSDate, - isAWSTime, isAWSDateTime, - isAWSTimestamp, isAWSEmail, + isAWSIPAddress, isAWSJSON, - isAWSURL, isAWSPhone, - isAWSIPAddress, - NAMESPACES, - extractPrimaryKeyFieldNames, + isAWSTime, + isAWSTimestamp, + isAWSURL, } from './util'; import { PredicateAll } from './predicates'; -import { InternalAPI } from '@aws-amplify/api/internals'; import { Adapter } from './storage/adapter'; -import { GraphQLAuthMode } from '@aws-amplify/core/internals/utils'; -export type Scalar = T extends Array ? InnerType : T; +export type Scalar = T extends (infer InnerType)[] ? InnerType : T; -//#region Schema types +// #region Schema types export type Schema = UserSchema & { version: string; codegenVersion: string; }; -export type UserSchema = { +export interface UserSchema { models: SchemaModels; nonModels?: SchemaNonModels; relationships?: RelationshipType; keys?: ModelKeys; enums: SchemaEnums; modelTopologicalOrdering?: Map; -}; -export type InternalSchema = { +} +export interface InternalSchema { namespaces: SchemaNamespaces; version: string; codegenVersion: string; -}; +} export type SchemaNamespaces = Record; export type SchemaNamespace = UserSchema & { name: string; }; export type SchemaModels = Record; -export type SchemaModel = { +export interface SchemaModel { name: string; pluralName: string; attributes?: ModelAttributes; @@ -60,71 +61,74 @@ export type SchemaModel = { allFields?: ModelFields; syncable?: boolean; -}; +} export function isSchemaModel(obj: any): obj is SchemaModel { - return obj && (obj).pluralName !== undefined; + return obj && (obj as SchemaModel).pluralName !== undefined; } export function isSchemaModelWithAttributes( - m: SchemaModel | SchemaNonModel + m: SchemaModel | SchemaNonModel, ): m is SchemaModel { return isSchemaModel(m) && (m as SchemaModel).attributes !== undefined; } export type SchemaNonModels = Record; -export type SchemaNonModel = { +export interface SchemaNonModel { name: string; fields: ModelFields; -}; +} type SchemaEnums = Record; -type SchemaEnum = { +interface SchemaEnum { name: string; values: string[]; -}; -export type ModelMeta = { +} +export interface ModelMeta { builder: PersistentModelConstructor; schema: SchemaModel; pkField: string[]; -}; +} export type ModelAssociation = AssociatedWith | TargetNameAssociation; -type AssociatedWith = { +interface AssociatedWith { connectionType: 'HAS_MANY' | 'HAS_ONE'; associatedWith: string | string[]; targetName?: string; targetNames?: string[]; -}; +} export function isAssociatedWith(obj: any): obj is AssociatedWith { return obj && obj.associatedWith; } -type TargetNameAssociation = { +interface TargetNameAssociation { connectionType: 'BELONGS_TO'; targetName?: string; targetNames?: string[]; -}; +} export function isTargetNameAssociation( - obj: any + obj: any, ): obj is TargetNameAssociation { return obj?.targetName || obj?.targetNames; } -type FieldAssociation = { +interface FieldAssociation { connectionType: 'HAS_ONE' | 'BELONGS_TO' | 'HAS_MANY'; -}; +} export function isFieldAssociation( obj: any, - fieldName: string + fieldName: string, ): obj is FieldAssociation { return obj?.fields[fieldName]?.association?.connectionType; } export type ModelAttributes = ModelAttribute[]; -export type ModelAttribute = { type: string; properties?: Record }; +export interface ModelAttribute { + type: string; + properties?: Record; +} -export type ModelAuthRule = { +export interface ModelAuthRule { allow: string; provider?: string; operations?: string[]; @@ -133,17 +137,17 @@ export type ModelAuthRule = { groups?: string[]; groupClaim?: string; groupsField?: string; -}; +} -export type ModelAttributeAuth = { +export interface ModelAttributeAuth { type: 'auth'; properties: { rules: ModelAuthRule[]; }; -}; +} export function isModelAttributeAuth( - attr: ModelAttribute + attr: ModelAttribute, ): attr is ModelAttributeAuth { return ( attr.type === 'auth' && @@ -153,32 +157,32 @@ export function isModelAttributeAuth( ); } -type ModelAttributeKey = { +interface ModelAttributeKey { type: 'key'; properties: { name?: string; fields: string[]; }; -}; +} -type ModelAttributePrimaryKey = { +interface ModelAttributePrimaryKey { type: 'key'; properties: { name: never; fields: string[]; }; -}; +} -type ModelAttributeCompositeKey = { +interface ModelAttributeCompositeKey { type: 'key'; properties: { name: string; fields: [string, string, string, string?, string?]; }; -}; +} export function isModelAttributeKey( - attr: ModelAttribute + attr: ModelAttribute, ): attr is ModelAttributeKey { return ( attr.type === 'key' && @@ -189,13 +193,13 @@ export function isModelAttributeKey( } export function isModelAttributePrimaryKey( - attr: ModelAttribute + attr: ModelAttribute, ): attr is ModelAttributePrimaryKey { return isModelAttributeKey(attr) && attr.properties.name === undefined; } export function isModelAttributeCompositeKey( - attr: ModelAttribute + attr: ModelAttribute, ): attr is ModelAttributeCompositeKey { return ( isModelAttributeKey(attr) && @@ -204,7 +208,7 @@ export function isModelAttributeCompositeKey( ); } -export type ModelAttributeAuthProperty = { +export interface ModelAttributeAuthProperty { allow: ModelAttributeAuthAllow; identityClaim?: string; groupClaim?: string; @@ -212,7 +216,7 @@ export type ModelAttributeAuthProperty = { operations?: string[]; ownerField?: string; provider?: ModelAttributeAuthProvider; -}; +} export enum ModelAttributeAuthAllow { CUSTOM = 'custom', @@ -247,13 +251,14 @@ export enum GraphQLScalarType { AWSPhone, AWSIPAddress, } - +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line @typescript-eslint/no-namespace export namespace GraphQLScalarType { export function getJSType( scalar: keyof Omit< typeof GraphQLScalarType, 'getJSType' | 'getValidationFunction' - > + >, ) { switch (scalar) { case 'Boolean': @@ -283,7 +288,7 @@ export namespace GraphQLScalarType { scalar: keyof Omit< typeof GraphQLScalarType, 'getJSType' | 'getValidationFunction' - > + >, ): ((val: string) => boolean) | ((val: number) => boolean) | undefined { switch (scalar) { case 'AWSDate': @@ -310,7 +315,7 @@ export namespace GraphQLScalarType { } } -export type AuthorizationRule = { +export interface AuthorizationRule { identityClaim: string; ownerField: string; provider: 'userPools' | 'oidc' | 'iam' | 'apiKey'; @@ -319,10 +324,10 @@ export type AuthorizationRule = { groupsField: string; authStrategy: 'owner' | 'groups' | 'private' | 'public'; areSubscriptionsPublic: boolean; -}; +} export function isGraphQLScalarType( - obj: any + obj: any, ): obj is keyof Omit< typeof GraphQLScalarType, 'getJSType' | 'getValidationFunction' @@ -330,12 +335,14 @@ export function isGraphQLScalarType( return obj && GraphQLScalarType[obj] !== undefined; } -export type ModelFieldType = { +export interface ModelFieldType { model: string; modelConstructor?: ModelMeta; -}; +} +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line unused-imports/no-unused-vars export function isModelFieldType( - obj: any + obj: any, ): obj is ModelFieldType { const modelField: keyof ModelFieldType = 'model'; if (obj && obj[modelField]) return true; @@ -343,7 +350,9 @@ export function isModelFieldType( return false; } -export type NonModelFieldType = { nonModel: string }; +export interface NonModelFieldType { + nonModel: string; +} export function isNonModelFieldType(obj: any): obj is NonModelFieldType { const typeField: keyof NonModelFieldType = 'nonModel'; if (obj && obj[typeField]) return true; @@ -351,7 +360,9 @@ export function isNonModelFieldType(obj: any): obj is NonModelFieldType { return false; } -type EnumFieldType = { enum: string }; +interface EnumFieldType { + enum: string; +} export function isEnumFieldType(obj: any): obj is EnumFieldType { const modelField: keyof EnumFieldType = 'enum'; if (obj && obj[modelField]) return true; @@ -359,7 +370,7 @@ export function isEnumFieldType(obj: any): obj is EnumFieldType { return false; } -export type ModelField = { +export interface ModelField { name: string; type: | keyof Omit< @@ -375,22 +386,20 @@ export type ModelField = { isArrayNullable?: boolean; association?: ModelAssociation; attributes?: ModelAttributes[]; -}; -//#endregion +} +// #endregion -//#region Model definition -export type NonModelTypeConstructor = { - new (init: T): T; -}; +// #region Model definition +export type NonModelTypeConstructor = new (init: T) => T; // Class for model -export type PersistentModelConstructor = { +export interface PersistentModelConstructor { new (init: ModelInit>): T; copyOf( src: T, - mutator: (draft: MutableModel>) => void + mutator: (draft: MutableModel>) => void, ): T; -}; +} /** * @private @@ -435,7 +444,7 @@ export type OptionallyManagedIdentifier = IdentifierBrand< >; // You provide the values -export type CompositeIdentifier> = IdentifierBrand< +export type CompositeIdentifier = IdentifierBrand< { fields: K; type: T }, 'CompositeIdentifier' >; @@ -486,10 +495,10 @@ export type IdentifierFieldsForInit< // Instance of model export declare const __modelMeta__: unique symbol; -export type PersistentModelMetaData = { +export interface PersistentModelMetaData { identifier?: Identifier; readOnlyFields?: string; -}; +} export interface AsyncCollection extends AsyncIterable { toArray(options?: { max?: number }): Promise; @@ -529,19 +538,23 @@ type OptionalRelativesOf = type OmitOptionalRelatives = Omit>; type PickOptionalRelatives = Pick>; +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line unused-imports/no-unused-vars type OmitOptionalFields = Omit< T, KeysOfSuperType | OptionalRelativesOf >; +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line unused-imports/no-unused-vars type PickOptionalFields = Pick< T, KeysOfSuperType | OptionalRelativesOf >; -export type DefaultPersistentModelMetaData = { +export interface DefaultPersistentModelMetaData { identifier: ManagedIdentifier<{ id: string }, 'id'>; readOnlyFields: never; -}; +} export type MetadataOrDefault< T extends PersistentModel, @@ -569,6 +582,8 @@ export type MetadataReadOnlyFields< // This type makes optional some identifiers in the constructor init object (e.g. OptionallyManagedIdentifier) export type ModelInitBase< T extends PersistentModel, + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types M extends PersistentModelMetaData = {}, > = Omit< T, @@ -583,6 +598,8 @@ export type ModelInitBase< export type ModelInit< T extends PersistentModel, + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types M extends PersistentModelMetaData = {}, > = { [P in keyof OmitOptionalRelatives>]: SettableFieldType< @@ -608,6 +625,8 @@ type DeepWritable = { export type MutableModel< T extends PersistentModel, + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/ban-types M extends PersistentModelMetaData = {}, // This provides Intellisense with ALL of the properties, regardless of read-only // but will throw a linting error if trying to overwrite a read-only property @@ -616,11 +635,11 @@ export type MutableModel< > & Readonly | MetadataReadOnlyFields>>; -export type ModelInstanceMetadata = { +export interface ModelInstanceMetadata { _version: number; _lastChangedAt: number; _deleted: boolean; -}; +} export type IdentifierFieldValue< T extends PersistentModel, @@ -638,7 +657,7 @@ export type IdentifierFieldOrIdentifierObject< export function isIdentifierObject( obj: any, - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): obj is IdentifierFields { const keys = extractPrimaryKeyFieldNames(modelDefinition); @@ -646,9 +665,9 @@ export function isIdentifierObject( typeof obj === 'object' && obj && keys.every(k => obj[k] !== undefined) ); } -//#endregion +// #endregion -//#region Subscription messages +// #region Subscription messages export enum OpType { INSERT = 'INSERT', UPDATE = 'UPDATE', @@ -660,21 +679,21 @@ export type SubscriptionMessage = Pick< 'opType' | 'element' | 'model' | 'condition' >; -export type InternalSubscriptionMessage = { +export interface InternalSubscriptionMessage { opType: OpType; element: T; model: PersistentModelConstructor; condition: PredicatesGroup | null; savedElement?: T; -}; +} -export type DataStoreSnapshot = { +export interface DataStoreSnapshot { items: T[]; isSynced: boolean; -}; -//#endregion +} +// #endregion -//#region Predicates +// #region Predicates export type PredicateExpression< M extends PersistentModel, @@ -683,14 +702,14 @@ export type PredicateExpression< ? ( operator: keyof MapTypeToOperands[TypeName], // make the operand type match the type they're trying to filter on - operand: MapTypeToOperands[TypeName][keyof MapTypeToOperands[TypeName]] + operand: MapTypeToOperands[TypeName][keyof MapTypeToOperands[TypeName]], ) => ModelPredicate : never; -type EqualityOperators = { +interface EqualityOperators { ne: T; eq: T; -}; +} type ScalarNumberOperators = EqualityOperators & { le: T; lt: T; @@ -706,22 +725,22 @@ type StringOperators = ScalarNumberOperators & { notContains: T; }; type BooleanOperators = EqualityOperators; -type ArrayOperators = { +interface ArrayOperators { contains: T; notContains: T; -}; +} export type AllOperators = NumberOperators & StringOperators & ArrayOperators; -type MapTypeToOperands = { +interface MapTypeToOperands { number: NumberOperators>; string: StringOperators>; boolean: BooleanOperators>; 'number[]': ArrayOperators; 'string[]': ArrayOperators; 'boolean[]': ArrayOperators; -}; +} type TypeName = T extends string ? 'string' @@ -737,58 +756,57 @@ type TypeName = T extends string ? 'boolean[]' : never; -export type PredicateGroups = { - and: ( - predicate: (predicate: ModelPredicate) => ModelPredicate - ) => ModelPredicate; - or: ( - predicate: (predicate: ModelPredicate) => ModelPredicate - ) => ModelPredicate; - not: ( - predicate: (predicate: ModelPredicate) => ModelPredicate - ) => ModelPredicate; -}; +export interface PredicateGroups { + and( + predicate: (predicate: ModelPredicate) => ModelPredicate, + ): ModelPredicate; + or( + predicate: (predicate: ModelPredicate) => ModelPredicate, + ): ModelPredicate; + not( + predicate: (predicate: ModelPredicate) => ModelPredicate, + ): ModelPredicate; +} export type ModelPredicate = { [K in keyof M]-?: PredicateExpression>; } & PredicateGroups; export type ProducerModelPredicate = ( - condition: ModelPredicate + condition: ModelPredicate, ) => ModelPredicate; -export type PredicatesGroup = { +export interface PredicatesGroup { type: keyof PredicateGroups; predicates: (PredicateObject | PredicatesGroup)[]; -}; +} export function isPredicateObj( - obj: any + obj: any, ): obj is PredicateObject { - return obj && (>obj).field !== undefined; + return obj && (obj as PredicateObject).field !== undefined; } export function isPredicateGroup( - obj: any + obj: any, ): obj is PredicatesGroup { - return obj && (>obj).type !== undefined; + return obj && (obj as PredicatesGroup).type !== undefined; } -export type PredicateObject = { +export interface PredicateObject { field: keyof T; operator: keyof AllOperators; operand: any; -}; +} export enum QueryOne { FIRST, LAST, } -export type GraphQLField = { - [field: string]: { - [operator: string]: string | number | [number, number]; - }; -}; +export type GraphQLField = Record< + string, + Record +>; export type GraphQLCondition = Partial< | GraphQLField @@ -812,29 +830,29 @@ export type GraphQLFilter = Partial< } >; -//#endregion +// #endregion -//#region Pagination +// #region Pagination -export type ProducerPaginationInput = { +export interface ProducerPaginationInput { sort?: ProducerSortPredicate; limit?: number; page?: number; -}; +} export type ObserveQueryOptions = Pick< ProducerPaginationInput, 'sort' >; -export type PaginationInput = { +export interface PaginationInput { sort?: SortPredicate; limit?: number; page?: number; -}; +} export type ProducerSortPredicate = ( - condition: SortPredicate + condition: SortPredicate, ) => SortPredicate; export type SortPredicate = { @@ -856,82 +874,81 @@ export enum SortDirection { export type SortPredicatesGroup = SortPredicateObject[]; -export type SortPredicateObject = { +export interface SortPredicateObject { field: keyof T; sortDirection: keyof typeof SortDirection; -}; +} -//#endregion +// #endregion -//#region System Components +// #region System Components -export type SystemComponent = { +export interface SystemComponent { setUp( schema: InternalSchema, namespaceResolver: NamespaceResolver, modelInstanceCreator: ModelInstanceCreator, getModelConstructorByModelName: ( namsespaceName: NAMESPACES, - modelName: string + modelName: string, ) => PersistentModelConstructor, - appId?: string + appId?: string, ): Promise; -}; +} export type NamespaceResolver = ( - modelConstructor: PersistentModelConstructor + modelConstructor: PersistentModelConstructor, ) => string; -export type ControlMessageType = { +export interface ControlMessageType { type: T; data?: any; -}; +} -//#endregion +// #endregion -//#region Relationship types -export type RelationType = { +// #region Relationship types +export interface RelationType { fieldName: string; modelName: string; relationType: 'HAS_ONE' | 'HAS_MANY' | 'BELONGS_TO'; targetName?: string; targetNames?: string[]; associatedWith?: string | string[]; -}; +} -type IndexOptions = { +interface IndexOptions { unique?: boolean; -}; +} -export type IndexesType = Array<[string, string[], IndexOptions?]>; +export type IndexesType = [string, string[], IndexOptions?][]; -export type RelationshipType = { - [modelName: string]: { +export type RelationshipType = Record< + string, + { indexes: IndexesType; relationTypes: RelationType[]; - }; -}; + } +>; -//#endregion +// #endregion -//#region Key type -export type KeyType = { +// #region Key type +export interface KeyType { primaryKey?: string[]; compositeKeys?: Set[]; -}; +} -export type ModelKeys = { - [modelName: string]: KeyType; -}; +export type ModelKeys = Record; -//#endregion +// #endregion -//#region DataStore config types -export type DataStoreConfig = { +// #region DataStore config types +export interface DataStoreConfig { DataStore?: { authModeStrategyType?: AuthModeStrategyType; conflictHandler?: ConflictHandler; // default : retry until client wins up to x times - errorHandler?: (error: SyncError) => void; // default : logger.warn + errorHandler?(error: SyncError): void; // default : logger.warn maxRecordsToSync?: number; // merge syncPageSize?: number; fullSyncInterval?: number; @@ -941,18 +958,18 @@ export type DataStoreConfig = { }; authModeStrategyType?: AuthModeStrategyType; conflictHandler?: ConflictHandler; // default : retry until client wins up to x times - errorHandler?: (error: SyncError) => void; // default : logger.warn + errorHandler?(error: SyncError): void; // default : logger.warn maxRecordsToSync?: number; // merge syncPageSize?: number; fullSyncInterval?: number; syncExpressions?: SyncExpression[]; authProviders?: AuthProviders; storageAdapter?: Adapter; -}; +} -export type AuthProviders = { - functionAuthProvider: () => { token: string } | Promise<{ token: string }>; -}; +export interface AuthProviders { + functionAuthProvider(): { token: string } | Promise<{ token: string }>; +} export enum AuthModeStrategyType { DEFAULT = 'DEFAULT', @@ -965,14 +982,14 @@ export type AuthModeStrategyReturn = | undefined | null; -export type AuthModeStrategyParams = { +export interface AuthModeStrategyParams { schema: InternalSchema; modelName: string; operation: ModelOperation; -}; +} export type AuthModeStrategy = ( - authModeStrategyParams: AuthModeStrategyParams + authModeStrategyParams: AuthModeStrategyParams, ) => AuthModeStrategyReturn | Promise; export enum ModelOperation { @@ -991,7 +1008,7 @@ export type ModelAuthModes = Record< export type SyncExpression = Promise<{ modelConstructor: any; - conditionProducer: (c?: any) => any; + conditionProducer(c?: any): any; }>; /* @@ -1013,14 +1030,14 @@ type Option0 = []; type Option1 = [V5ModelPredicate | undefined]; type Option = Option0 | Option1; -type Lookup = { +interface Lookup { 0: | ModelPredicateExtender | Promise> | typeof PredicateAll | Promise; 1: PredicateInternalsKey | undefined; -}; +} type ConditionProducer> = ( ...args: A @@ -1031,7 +1048,7 @@ export async function syncExpression< A extends Option, >( modelConstructor: PersistentModelConstructor, - conditionProducer: ConditionProducer + conditionProducer: ConditionProducer, ): Promise<{ modelConstructor: PersistentModelConstructor; conditionProducer: ConditionProducer; @@ -1042,15 +1059,15 @@ export async function syncExpression< }; } -export type SyncConflict = { +export interface SyncConflict { modelConstructor: PersistentModelConstructor; localModel: PersistentModel; remoteModel: PersistentModel; operation: OpType; attempts: number; -}; +} -export type SyncError = { +export interface SyncError { message: string; errorType: ErrorType; errorInfo?: string; @@ -1061,7 +1078,7 @@ export type SyncError = { process: ProcessName; operation: string; cause?: Error; -}; +} export type ErrorType = | 'ConfigError' @@ -1080,28 +1097,28 @@ export enum ProcessName { export const DISCARD = Symbol('DISCARD'); export type ConflictHandler = ( - conflict: SyncConflict + conflict: SyncConflict, ) => | Promise | PersistentModel | typeof DISCARD; export type ErrorHandler = (error: SyncError) => void; -export type DeferredCallbackResolverOptions = { - callback: () => void; +export interface DeferredCallbackResolverOptions { + callback(): void; maxInterval?: number; - errorHandler?: (error: string) => void; -}; + errorHandler?(error: string): void; +} export enum LimitTimerRaceResolvedValues { LIMIT = 'LIMIT', TIMER = 'TIMER', } -//#endregion +// #endregion -export type AmplifyContext = { +export interface AmplifyContext { InternalAPI: typeof InternalAPI; -}; +} // #region V5 predicate types @@ -1145,7 +1162,7 @@ export type WithoutNevers = Pick>; * ``` */ export type RecursiveModelPredicateExtender = ( - lambda: RecursiveModelPredicate + lambda: RecursiveModelPredicate, ) => PredicateInternalsKey; export type RecursiveModelPredicateAggregateExtender< @@ -1153,11 +1170,11 @@ export type RecursiveModelPredicateAggregateExtender< > = (lambda: RecursiveModelPredicate) => PredicateInternalsKey[]; export type RecursiveModelPredicateOperator = ( - predicates: RecursiveModelPredicateAggregateExtender + predicates: RecursiveModelPredicateAggregateExtender, ) => PredicateInternalsKey; export type RecursiveModelPredicateNegation = ( - predicate: RecursiveModelPredicateExtender + predicate: RecursiveModelPredicateExtender, ) => PredicateInternalsKey; export type RecursiveModelPredicate = { @@ -1192,21 +1209,23 @@ export type RecursiveModelPredicate = { * ``` */ export type ModelPredicateExtender = ( - lambda: V5ModelPredicate + lambda: V5ModelPredicate, ) => PredicateInternalsKey; export type ModelPredicateAggregateExtender = ( - lambda: V5ModelPredicate + lambda: V5ModelPredicate, ) => PredicateInternalsKey[]; export type ValuePredicate< + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line unused-imports/no-unused-vars RT extends PersistentModel, MT extends MatchableTypes, > = { [K in AllFieldOperators]: K extends 'between' ? ( inclusiveLowerBound: Scalar, - inclusiveUpperBound: Scalar + inclusiveUpperBound: Scalar, ) => PredicateInternalsKey : (operand: Scalar) => PredicateInternalsKey; }; @@ -1222,11 +1241,11 @@ export type V5ModelPredicate = WithoutNevers<{ } & PredicateInternalsKey; export type ModelPredicateOperator = ( - predicates: ModelPredicateAggregateExtender + predicates: ModelPredicateAggregateExtender, ) => PredicateInternalsKey; export type ModelPredicateNegation = ( - predicate: ModelPredicateExtender + predicate: ModelPredicateExtender, ) => PredicateInternalsKey; /** @@ -1234,7 +1253,7 @@ export type ModelPredicateNegation = ( * that should not be exposed on public customer interfaces. */ export class PredicateInternalsKey { - private __isPredicateInternalsKeySentinel: boolean = true; + private __isPredicateInternalsKeySentinel = true; } // #endregion diff --git a/packages/datastore/src/util.ts b/packages/datastore/src/util.ts index 088a731b5e1..8f745293dc1 100644 --- a/packages/datastore/src/util.ts +++ b/packages/datastore/src/util.ts @@ -1,40 +1,41 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { monotonicFactory, ULID } from 'ulid'; +import { ULID, monotonicFactory } from 'ulid'; import { - amplifyUuid, AmplifyUrl, WordArray, + amplifyUuid, } from '@aws-amplify/core/internals/utils'; -import { produce, applyPatches, Patch } from 'immer'; +import { Patch, applyPatches, produce } from 'immer'; + import { ModelInstanceCreator } from './datastore/datastore'; import { AllOperators, - isPredicateGroup, - isPredicateObj, + DeferredCallbackResolverOptions, + IndexesType, + LimitTimerRaceResolvedValues, + ModelAssociation, + ModelAttribute, + ModelAttributes, + ModelKeys, + NonModelTypeConstructor, + PaginationInput, PersistentModel, PersistentModelConstructor, PredicateGroups, PredicateObject, PredicatesGroup, - RelationshipType, RelationType, - ModelKeys, - ModelAttributes, + RelationshipType, + SchemaModel, SchemaNamespace, - SortPredicatesGroup, SortDirection, + SortPredicatesGroup, + isModelAttributeCompositeKey, isModelAttributeKey, isModelAttributePrimaryKey, - isModelAttributeCompositeKey, - NonModelTypeConstructor, - PaginationInput, - DeferredCallbackResolverOptions, - LimitTimerRaceResolvedValues, - SchemaModel, - ModelAttribute, - IndexesType, - ModelAssociation, + isPredicateGroup, + isPredicateObj, } from './types'; import { ModelSortPredicateCreator } from './predicates'; @@ -73,14 +74,14 @@ export enum NAMESPACES { STORAGE = 'storage', } -const DATASTORE = NAMESPACES.DATASTORE; -const USER = NAMESPACES.USER; -const SYNC = NAMESPACES.SYNC; -const STORAGE = NAMESPACES.STORAGE; +const { DATASTORE } = NAMESPACES; +const { USER } = NAMESPACES; +const { SYNC } = NAMESPACES; +const { STORAGE } = NAMESPACES; export { USER, SYNC, STORAGE, DATASTORE }; -export const exhaustiveCheck = (obj: never, throwOnError: boolean = true) => { +export const exhaustiveCheck = (obj: never, throwOnError = true) => { if (throwOnError) { throw new Error(`Invalid ${obj}`); } @@ -93,7 +94,7 @@ export const isNullOrUndefined = (val: any): boolean => { export const validatePredicate = ( model: T, groupType: keyof PredicateGroups, - predicatesOrGroups: (PredicateObject | PredicatesGroup)[] + predicatesOrGroups: (PredicateObject | PredicatesGroup)[], ) => { let filterType: keyof Pick; let isNegation = false; @@ -127,6 +128,7 @@ export const validatePredicate = ( if (isPredicateGroup(predicateOrGroup)) { const { type, predicates } = predicateOrGroup; + return validatePredicate(model, type, predicates); } @@ -139,7 +141,7 @@ export const validatePredicate = ( export const validatePredicateField = ( value: T, operator: keyof AllOperators, - operand: T | [T, T] + operand: T | [T, T], ) => { switch (operator) { case 'ne': @@ -154,23 +156,26 @@ export const validatePredicateField = ( return value >= operand; case 'gt': return value > operand; - case 'between': - const [min, max] = <[T, T]>operand; + case 'between': { + const [min, max] = operand as [T, T]; + return value >= min && value <= max; + } case 'beginsWith': return ( !isNullOrUndefined(value) && - ((value)).startsWith((operand)) + (value as unknown as string).startsWith(operand as unknown as string) ); case 'contains': return ( !isNullOrUndefined(value) && - ((value)).indexOf((operand)) > -1 + (value as unknown as string).indexOf(operand as unknown as string) > -1 ); case 'notContains': return ( isNullOrUndefined(value) || - ((value)).indexOf((operand)) === -1 + (value as unknown as string).indexOf(operand as unknown as string) === + -1 ); default: return false; @@ -178,10 +183,10 @@ export const validatePredicateField = ( }; export const isModelConstructor = ( - obj: any + obj: any, ): obj is PersistentModelConstructor => { return ( - obj && typeof (>obj).copyOf === 'function' + obj && typeof (obj as PersistentModelConstructor).copyOf === 'function' ); }; @@ -192,7 +197,7 @@ export function registerNonModelClass(clazz: NonModelTypeConstructor) { } export const isNonModelConstructor = ( - obj: any + obj: any, ): obj is NonModelTypeConstructor => { return nonModelClasses.has(obj); }; @@ -206,12 +211,12 @@ export const traverseModel = ( modelInstanceCreator: ModelInstanceCreator, getModelConstructorByModelName: ( namsespaceName: NAMESPACES, - modelName: string - ) => PersistentModelConstructor + modelName: string, + ) => PersistentModelConstructor, ) => { const modelConstructor = getModelConstructorByModelName( namespace.name as NAMESPACES, - srcModelName + srcModelName, ); const result: { @@ -220,7 +225,9 @@ export const traverseModel = ( instance: T; }[] = []; - const newInstance = modelConstructor.copyOf(instance, () => {}); + const newInstance = modelConstructor.copyOf(instance, () => { + // no-op + }); result.unshift({ modelName: srcModelName, @@ -231,7 +238,7 @@ export const traverseModel = ( if (!topologicallySortedModels.has(namespace)) { topologicallySortedModels.set( namespace, - Array.from(namespace.modelTopologicalOrdering!.keys()) + Array.from(namespace.modelTopologicalOrdering!.keys()), ); } @@ -251,6 +258,8 @@ let privateModeCheckResult; export const isPrivateMode = () => { return new Promise(resolve => { const dbname = amplifyUuid(); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line prefer-const let db; const isPrivate = () => { @@ -268,7 +277,7 @@ export const isPrivateMode = () => { privateModeCheckResult = true; - return resolve(false); + resolve(false); }; if (privateModeCheckResult === true) { @@ -276,10 +285,16 @@ export const isPrivateMode = () => { } if (privateModeCheckResult === false) { - return isPrivate(); + isPrivate(); + + return; } - if (indexedDB === null) return isPrivate(); + if (indexedDB === null) { + isPrivate(); + + return; + } db = indexedDB.open(dbname); db.onerror = isPrivate; @@ -313,19 +328,23 @@ export const isSafariCompatabilityMode: () => Promise = async () => { const db: IDBDatabase | false = await new Promise(resolve => { const dbOpenRequest = indexedDB.open(dbName); - dbOpenRequest.onerror = () => resolve(false); + dbOpenRequest.onerror = () => { + resolve(false); + }; dbOpenRequest.onsuccess = () => { - const db = dbOpenRequest.result; - resolve(db); + const resultDB = dbOpenRequest.result; + resolve(resultDB); }; dbOpenRequest.onupgradeneeded = (event: any) => { - const db = event?.target?.result; + const resultDB = event?.target?.result; - db.onerror = () => resolve(false); + resultDB.onerror = () => { + resolve(false); + }; - const store = db.createObjectStore(storeName, { + const store = resultDB.createObjectStore(storeName, { autoIncrement: true, }); @@ -352,7 +371,9 @@ export const isSafariCompatabilityMode: () => Promise = async () => { const getRequest = index.get([1]); - getRequest.onerror = () => resolve(false); + getRequest.onerror = () => { + resolve(false); + }; getRequest.onsuccess = (event: any) => { resolve(event?.target?.result); @@ -360,6 +381,8 @@ export const isSafariCompatabilityMode: () => Promise = async () => { }); if (db && typeof db.close === 'function') { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression await db.close(); } @@ -400,7 +423,7 @@ const getBytesFromHex = (encoded: string): Uint8Array => { out[i / 2] = HEX_TO_SHORT[encodedByte]; } else { throw new Error( - `Cannot decode unrecognized sequence ${encodedByte} as hexadecimal` + `Cannot decode unrecognized sequence ${encodedByte} as hexadecimal`, ); } } @@ -449,7 +472,7 @@ export function getNow() { } export function sortCompareFunction( - sortPredicates: SortPredicatesGroup + sortPredicates: SortPredicatesGroup, ) { return function compareFunction(a, b) { // enable multi-field sort by iterating over predicates until @@ -478,11 +501,7 @@ export function sortCompareFunction( // returns true if equal by value // if nullish is true, treat undefined and null values as equal // to normalize for GQL response values for undefined fields -export function valuesEqual( - valA: any, - valB: any, - nullish: boolean = false -): boolean { +export function valuesEqual(valA: any, valB: any, nullish = false): boolean { let a = valA; let b = valB; @@ -561,12 +580,12 @@ export function valuesEqual( */ export function inMemoryPagination( records: T[], - pagination?: PaginationInput + pagination?: PaginationInput, ): T[] { if (pagination && records.length > 1) { if (pagination.sort) { const sortPredicates = ModelSortPredicateCreator.getPredicates( - pagination.sort + pagination.sort, ); if (sortPredicates.length) { @@ -581,6 +600,7 @@ export function inMemoryPagination( return records.slice(start, end); } + return records; } @@ -593,13 +613,14 @@ export function inMemoryPagination( */ export async function asyncSome( items: Record[], - matches: (item: Record) => Promise + matches: (item: Record) => Promise, ): Promise { for (const item of items) { if (await matches(item)) { return true; } } + return false; } @@ -612,13 +633,14 @@ export async function asyncSome( */ export async function asyncEvery( items: Record[], - matches: (item: Record) => Promise + matches: (item: Record) => Promise, ): Promise { for (const item of items) { if (!(await matches(item))) { return false; } } + return true; } @@ -632,7 +654,7 @@ export async function asyncEvery( */ export async function asyncFilter( items: T[], - matches: (item: T) => Promise + matches: (item: T) => Promise, ): Promise { const results: T[] = []; for (const item of items) { @@ -640,6 +662,7 @@ export async function asyncFilter( results.push(item); } } + return results; } @@ -649,13 +672,13 @@ export const isAWSDate = (val: string): boolean => { export const isAWSTime = (val: string): boolean => { return !!/^\d{2}:\d{2}(:\d{2}(.\d+)?)?(Z|[+-]\d{2}:\d{2}($|:\d{2}))?$/.exec( - val + val, ); }; export const isAWSDateTime = (val: string): boolean => { return !!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(.\d+)?)?(Z|[+-]\d{2}:\d{2}($|:\d{2}))?$/.exec( - val + val, ); }; @@ -665,13 +688,14 @@ export const isAWSTimestamp = (val: number): boolean => { export const isAWSEmail = (val: string): boolean => { return !!/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.exec( - val + val, ); }; export const isAWSJSON = (val: string): boolean => { try { JSON.parse(val); + return true; } catch { return false; @@ -692,7 +716,7 @@ export const isAWSPhone = (val: string): boolean => { export const isAWSIPAddress = (val: string): boolean => { return !!/((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$))$/.exec( - val + val, ); }; @@ -701,12 +725,14 @@ export class DeferredPromise { public resolve: (value: string | PromiseLike) => void; public reject: () => void; constructor() { + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; this.promise = new Promise( (resolve: (value: string | PromiseLike) => void, reject) => { self.resolve = resolve; self.reject = reject; - } + }, ); } } @@ -717,10 +743,13 @@ export class DeferredCallbackResolver { private maxInterval: number; private timer: ReturnType; private raceInFlight = false; - private callback = () => {}; + private callback = () => { + // no-op + }; + private errorHandler: (error: string) => void; private defaultErrorHandler = ( - msg = 'DeferredCallbackResolver error' + msg = 'DeferredCallbackResolver error', ): void => { throw new Error(msg); }; @@ -732,7 +761,7 @@ export class DeferredCallbackResolver { } private startTimer(): void { - this.timerPromise = new Promise((resolve, reject) => { + this.timerPromise = new Promise((resolve, _reject) => { this.timer = setTimeout(() => { resolve(LimitTimerRaceResolvedValues.TIMER); }, this.maxInterval); @@ -757,6 +786,8 @@ export class DeferredCallbackResolver { this.raceInFlight = false; this.limitPromise = new DeferredPromise(); + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line no-unsafe-finally return winner!; } } @@ -794,7 +825,7 @@ export class DeferredCallbackResolver { export function mergePatches( originalSource: T, oldPatches: Patch[], - newPatches: Patch[] + newPatches: Patch[], ): Patch[] { const patchesToMerge = oldPatches.concat(newPatches); let patches: Patch[]; @@ -805,8 +836,9 @@ export function mergePatches( }, p => { patches = p; - } + }, ); + return patches!; } @@ -816,7 +848,7 @@ export const getStorename = (namespace: string, modelName: string) => { return storeName; }; -//#region Key Utils +// #region Key Utils /* When we have GSI(s) with composite sort keys defined on a model @@ -851,7 +883,7 @@ export const getStorename = (namespace: string, modelName: string) => { See 'processCompositeKeys' test in util.test.ts for more examples */ export const processCompositeKeys = ( - attributes: ModelAttributes + attributes: ModelAttributes, ): Set[] => { const extractCompositeSortKey = ({ properties: { @@ -874,6 +906,7 @@ export const processCompositeKeys = ( if (combined.length === 0) { combined.push(sortKeyFieldsSet); + return combined; } @@ -906,7 +939,7 @@ export const processCompositeKeys = ( }; export const extractKeyIfExists = ( - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): ModelAttribute | undefined => { const keyAttribute = modelDefinition?.attributes?.find(isModelAttributeKey); @@ -914,7 +947,7 @@ export const extractKeyIfExists = ( }; export const extractPrimaryKeyFieldNames = ( - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): string[] => { const keyAttribute = extractKeyIfExists(modelDefinition); if (keyAttribute && isModelAttributePrimaryKey(keyAttribute)) { @@ -926,17 +959,18 @@ export const extractPrimaryKeyFieldNames = ( export const extractPrimaryKeyValues = ( model: T, - keyFields: string[] + keyFields: string[], ): string[] => { return keyFields.map(key => model[key]); }; export const extractPrimaryKeysAndValues = ( model: T, - keyFields: string[] + keyFields: string[], ): any => { const primaryKeysAndValues = {}; keyFields.forEach(key => (primaryKeysAndValues[key] = model[key])); + return primaryKeysAndValues; }; @@ -955,7 +989,7 @@ export const isIdManaged = (modelDefinition: SchemaModel): boolean => { // IdentifierFields // @primaryKey with explicit `id` in the PK. Single key or composite export const isIdOptionallyManaged = ( - modelDefinition: SchemaModel + modelDefinition: SchemaModel, ): boolean => { const keyAttribute = extractKeyIfExists(modelDefinition); @@ -967,7 +1001,7 @@ export const isIdOptionallyManaged = ( }; export const establishRelationAndKeys = ( - namespace: SchemaNamespace + namespace: SchemaNamespace, ): [RelationshipType, ModelKeys] => { const relationship: RelationshipType = {}; const keys: ModelKeys = {}; @@ -983,25 +1017,31 @@ export const establishRelationAndKeys = ( typeof fieldAttribute.type === 'object' && 'model' in fieldAttribute.type ) { - const connectionType = fieldAttribute.association!.connectionType; + const { connectionType } = fieldAttribute.association!; relationship[mKey].relationTypes.push({ fieldName: fieldAttribute.name, modelName: fieldAttribute.type.model, relationType: connectionType, + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line dot-notation targetName: fieldAttribute.association!['targetName'], + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line dot-notation targetNames: fieldAttribute.association!['targetNames'], + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line dot-notation associatedWith: fieldAttribute.association!['associatedWith'], }); if (connectionType === 'BELONGS_TO') { const targetNames = extractTargetNamesFromSrc( - fieldAttribute.association + fieldAttribute.association, ); if (targetNames) { const idxName = indexNameFromKeys(targetNames); const idxExists = relationship[mKey].indexes.find( - ([index]) => index === idxName + ([index]) => index === idxName, ); if (!idxExists) { @@ -1030,7 +1070,7 @@ export const establishRelationAndKeys = ( // create indexes for all other keys const idxName = indexNameFromKeys(fields); const idxExists = relationship[mKey].indexes.find( - ([index]) => index === idxName + ([index]) => index === idxName, ); if (!idxExists) { @@ -1057,22 +1097,26 @@ export const establishRelationAndKeys = ( export const getIndex = ( rel: RelationType[], - src: string + src: string, ): string | undefined => { let indexName; + // TODO(eslint): remove this linter suppression with refactoring. + // eslint-disable-next-line array-callback-return rel.some((relItem: RelationType) => { if (relItem.modelName === src) { const targetNames = extractTargetNamesFromSrc(relItem); indexName = targetNames && indexNameFromKeys(targetNames); + return true; } }); + return indexName; }; export const getIndexFromAssociation = ( indexes: IndexesType, - src: string | string[] + src: string | string[], ): string | undefined => { let indexName: string; @@ -1083,6 +1127,7 @@ export const getIndexFromAssociation = ( } const associationIndex = indexes.find(([idxName]) => idxName === indexName); + return associationIndex && associationIndex[0]; }; @@ -1094,7 +1139,7 @@ the single field `targetName` has been replaced with an array of `targetNames`. * @returns array of targetNames, or `undefined` */ export const extractTargetNamesFromSrc = ( - src: RelationType | ModelAssociation | undefined + src: RelationType | ModelAssociation | undefined, ): string[] | undefined => { const targetName = src?.targetName; const targetNames = src?.targetNames; @@ -1115,6 +1160,7 @@ export const indexNameFromKeys = (keys: string[]): string => { if (idx === 0) { return cur; } + return `${prev}${IDENTIFIER_KEY_SEPARATOR}${cur}`; }, ''); }; @@ -1130,7 +1176,7 @@ export const keysEqual = (keysA, keysB): boolean => { // Returns primary keys for a model export const getIndexKeys = ( namespace: SchemaNamespace, - modelName: string + modelName: string, ): string[] => { const keyPath = namespace?.keys?.[modelName]?.primaryKey; @@ -1141,7 +1187,7 @@ export const getIndexKeys = ( return [ID]; }; -//#endregion +// #endregion /** * Determine what the managed timestamp field names are for the given model definition @@ -1156,10 +1202,10 @@ export const getIndexKeys = ( * @returns An object mapping `createdAt` and `updatedAt` to their field names. */ export const getTimestampFields = ( - definition: SchemaModel + definition: SchemaModel, ): { createdAt: string; updatedAt: string } => { const modelAttributes = definition.attributes?.find( - attr => attr.type === 'model' + attr => attr.type === 'model', ); const timestampFieldsMap = modelAttributes?.properties?.timestamps; diff --git a/packages/datastore/tsconfig.build.json b/packages/datastore/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/datastore/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/datastore/tsconfig.json b/packages/datastore/tsconfig.json index cba85ceb461..f221571c25e 100755 --- a/packages/datastore/tsconfig.json +++ b/packages/datastore/tsconfig.json @@ -1,10 +1,14 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { "importHelpers": false, "strict": false, "noImplicitAny": false, - "skipLibCheck": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/datastore/tsconfig.test.json b/packages/datastore/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/datastore/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/datastore/tsconfig.watch.json b/packages/datastore/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/datastore/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/datastore/tslint.json b/packages/datastore/tslint.json deleted file mode 100644 index 081fa33ae8f..00000000000 --- a/packages/datastore/tslint.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/geo/.eslintrc.js b/packages/geo/.eslintrc.js new file mode 100644 index 00000000000..c720644cfa8 --- /dev/null +++ b/packages/geo/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/geo', + }, + ], + }, +}; diff --git a/packages/geo/jest.config.js b/packages/geo/jest.config.js deleted file mode 100644 index 793f297724a..00000000000 --- a/packages/geo/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 86, - functions: 95, - lines: 89, - statements: 89, - }, - }, - moduleNameMapper: { - uuid: require.resolve('uuid'), - }, -}; diff --git a/packages/geo/jest.config.mjs b/packages/geo/jest.config.mjs new file mode 100644 index 00000000000..9fc759c6691 --- /dev/null +++ b/packages/geo/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 86, + functions: 95, + lines: 89, + statements: 89, + }, + }, + moduleNameMapper: { + uuid: requireResolve('uuid'), + }, +}); + +export default jestConfig; diff --git a/packages/geo/package.json b/packages/geo/package.json index 35ce36f6c7a..ea6984eeb16 100644 --- a/packages/geo/package.json +++ b/packages/geo/package.json @@ -14,19 +14,20 @@ "./dist/esm/geo/geo.mjs" ], "scripts": { - "test": "yarn run lint && jest -w 1 --coverage --logHeapUsage", + "test": "yarn run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint '{__tests__,src}/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 84.00" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 84.00" }, "exports": { ".": { @@ -78,8 +79,7 @@ "devDependencies": { "@aws-amplify/core": "6.0.8", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/geo/src/Geo.ts b/packages/geo/src/Geo.ts index 504973bdbb7..e62a019c9ae 100644 --- a/packages/geo/src/Geo.ts +++ b/packages/geo/src/Geo.ts @@ -1,26 +1,25 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Amplify, ConsoleLogger } from '@aws-amplify/core'; -import { AmazonLocationServiceProvider } from './providers/location-service/AmazonLocationServiceProvider'; +import { AmazonLocationServiceProvider } from './providers/location-service/AmazonLocationServiceProvider'; import { validateCoordinates } from './util'; - import { - Place, - GeoConfig, Coordinates, - SearchByTextOptions, - SearchByCoordinatesOptions, + DeleteGeofencesResults, + GeoConfig, GeoProvider, - MapStyle, + Geofence, GeofenceId, GeofenceInput, GeofenceOptions, - SaveGeofencesResults, - Geofence, ListGeofenceOptions, ListGeofenceResults, - DeleteGeofencesResults, + MapStyle, + Place, + SaveGeofencesResults, + SearchByCoordinatesOptions, + SearchByTextOptions, searchByPlaceIdOptions, } from './types'; @@ -43,7 +42,7 @@ export class GeoClass { this._config = Object.assign({}, this._config, amplifyConfig.Geo); const locationProvider = new AmazonLocationServiceProvider( - amplifyConfig.Geo + amplifyConfig.Geo, ); this._pluggables.push(locationProvider); @@ -74,7 +73,7 @@ export class GeoClass { */ public getPluggable(providerName: string) { const pluggable = this._pluggables.find( - pluggable => pluggable.getProviderName() === providerName + _pluggable => _pluggable.getProviderName() === providerName, ); if (pluggable === undefined) { logger.debug('No plugin found with providerName', providerName); @@ -88,9 +87,8 @@ export class GeoClass { */ public removePluggable(providerName: string) { this._pluggables = this._pluggables.filter( - pluggable => pluggable.getProviderName() !== providerName + pluggable => pluggable.getProviderName() !== providerName, ); - return; } /** @@ -123,7 +121,7 @@ export class GeoClass { */ public async searchByText( text: string, - options?: SearchByTextOptions + options?: SearchByTextOptions, ): Promise { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); @@ -144,7 +142,7 @@ export class GeoClass { */ public async searchForSuggestions( text: string, - options?: SearchByTextOptions + options?: SearchByTextOptions, ) { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); @@ -165,7 +163,7 @@ export class GeoClass { */ public async searchByPlaceId( placeId: string, - options?: searchByPlaceIdOptions + options?: searchByPlaceIdOptions, ) { const providerName = DEFAULT_PROVIDER; const prov = this.getPluggable(providerName); @@ -186,7 +184,7 @@ export class GeoClass { */ public async searchByCoordinates( coordinates: Coordinates, - options?: SearchByCoordinatesOptions + options?: SearchByCoordinatesOptions, ): Promise { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); @@ -194,6 +192,7 @@ export class GeoClass { const [lng, lat] = coordinates; try { validateCoordinates(lng, lat); + return await prov.searchByCoordinates(coordinates, options); } catch (error) { logger.debug(error); @@ -211,7 +210,7 @@ export class GeoClass { */ public async saveGeofences( geofences: GeofenceInput | GeofenceInput[], - options?: GeofenceOptions + options?: GeofenceOptions, ): Promise { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); @@ -240,7 +239,7 @@ export class GeoClass { */ public async getGeofence( geofenceId: GeofenceId, - options?: GeofenceOptions + options?: GeofenceOptions, ): Promise { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); @@ -261,7 +260,7 @@ export class GeoClass { * nextToken: token for next page of geofences */ public async listGeofences( - options?: ListGeofenceOptions + options?: ListGeofenceOptions, ): Promise { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); @@ -284,7 +283,7 @@ export class GeoClass { */ public async deleteGeofences( geofenceIds: string | string[], - options?: GeofenceOptions + options?: GeofenceOptions, ): Promise { const { providerName = DEFAULT_PROVIDER } = options || {}; const prov = this.getPluggable(providerName); diff --git a/packages/geo/src/providers/location-service/AmazonLocationServiceProvider.ts b/packages/geo/src/providers/location-service/AmazonLocationServiceProvider.ts index 79488643d50..a153589e33d 100644 --- a/packages/geo/src/providers/location-service/AmazonLocationServiceProvider.ts +++ b/packages/geo/src/providers/location-service/AmazonLocationServiceProvider.ts @@ -1,65 +1,62 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import camelcaseKeys from 'camelcase-keys'; - -import { Amplify, fetchAuthSession, ConsoleLogger } from '@aws-amplify/core'; +import { Amplify, ConsoleLogger, fetchAuthSession } from '@aws-amplify/core'; import { GeoAction } from '@aws-amplify/core/internals/utils'; import { - Place as PlaceResult, - LocationClient, - SearchPlaceIndexForTextCommand, - SearchPlaceIndexForTextCommandInput, - SearchPlaceIndexForSuggestionsCommand, - SearchPlaceIndexForSuggestionsCommandInput, - SearchPlaceIndexForPositionCommand, - SearchPlaceIndexForPositionCommandInput, + BatchDeleteGeofenceCommand, + BatchDeleteGeofenceCommandInput, + BatchDeleteGeofenceCommandOutput, BatchPutGeofenceCommand, BatchPutGeofenceCommandInput, - BatchPutGeofenceRequestEntry, BatchPutGeofenceCommandOutput, - GetPlaceCommand, - GetPlaceCommandInput, - GetPlaceCommandOutput, + BatchPutGeofenceRequestEntry, GetGeofenceCommand, GetGeofenceCommandInput, GetGeofenceCommandOutput, + GetPlaceCommand, + GetPlaceCommandInput, + GetPlaceCommandOutput, ListGeofencesCommand, ListGeofencesCommandInput, ListGeofencesCommandOutput, - BatchDeleteGeofenceCommand, - BatchDeleteGeofenceCommandInput, - BatchDeleteGeofenceCommandOutput, + LocationClient, + Place as PlaceResult, + SearchPlaceIndexForPositionCommand, + SearchPlaceIndexForPositionCommandInput, + SearchPlaceIndexForSuggestionsCommand, + SearchPlaceIndexForSuggestionsCommandInput, + SearchPlaceIndexForTextCommand, + SearchPlaceIndexForTextCommandInput, } from '@aws-sdk/client-location'; - import { getGeoUserAgent, mapSearchOptions, validateGeofenceId, validateGeofencesInput, -} from '../../util'; - +} from '~/src/util'; import { - GeoConfig, - SearchByTextOptions, - SearchByCoordinatesOptions, - GeoProvider, - Place, + AmazonLocationServiceBatchGeofenceErrorMessages, + AmazonLocationServiceDeleteGeofencesResults, + AmazonLocationServiceGeofence, + AmazonLocationServiceGeofenceOptions, + AmazonLocationServiceGeofenceStatus, + AmazonLocationServiceListGeofenceOptions, AmazonLocationServiceMapStyle, Coordinates, - SearchForSuggestionsResults, + GeoConfig, + GeoProvider, GeofenceId, GeofenceInput, - AmazonLocationServiceGeofenceOptions, - AmazonLocationServiceListGeofenceOptions, + GeofencePolygon, ListGeofenceResults, - AmazonLocationServiceGeofenceStatus, + Place, SaveGeofencesResults, - AmazonLocationServiceGeofence, - GeofencePolygon, - AmazonLocationServiceDeleteGeofencesResults, + SearchByCoordinatesOptions, + SearchByTextOptions, + SearchForSuggestionsResults, searchByPlaceIdOptions, - AmazonLocationServiceBatchGeofenceErrorMessages, -} from '../../types'; +} from '~/src/types'; const logger = new ConsoleLogger('AmazonLocationServiceProvider'); @@ -78,7 +75,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { * @param {Object} config - Configuration object for Geo */ constructor(config?: GeoConfig) { - this._config = config ? config : {}; + this._config = config || {}; logger.debug('Geo Options', this._config); } @@ -107,10 +104,10 @@ export class AmazonLocationServiceProvider implements GeoProvider { const mapStyles: AmazonLocationServiceMapStyle[] = []; const availableMaps = this._config.maps.items; - const region = this._config.region; + const { region } = this._config; for (const mapName in availableMaps) { - const style = availableMaps[mapName].style; + const { style } = availableMaps[mapName]; mapStyles.push({ mapName, style, region }); } @@ -125,8 +122,8 @@ export class AmazonLocationServiceProvider implements GeoProvider { this._verifyMapResources(); const mapName = this._config.maps.default; - const style = this._config.maps.items[mapName].style; - const region = this._config.region; + const { style } = this._config.maps.items[mapName]; + const { region } = this._config; return { mapName, style, region }; } @@ -139,7 +136,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { */ public async searchByText( text: string, - options?: SearchByTextOptions + options?: SearchByTextOptions, ): Promise { const credentialsOK = await this._ensureCredentials(); if (!credentialsOK) { @@ -187,7 +184,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { * Here we want to flatten that to an array of results and change them to camelCase */ const PascalResults: PlaceResult[] = response.Results.map( - result => result.Place + result => result.Place, ); const results: Place[] = camelcaseKeys(PascalResults, { deep: true, @@ -205,7 +202,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { public async searchForSuggestions( text: string, - options?: SearchByTextOptions + options?: SearchByTextOptions, ): Promise { const credentialsOK = await this._ensureCredentials(); if (!credentialsOK) { @@ -238,7 +235,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { customUserAgent: getGeoUserAgent(GeoAction.SearchForSuggestions), }); const command = new SearchPlaceIndexForSuggestionsCommand( - locationServiceInput + locationServiceInput, ); let response; @@ -270,7 +267,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { public async searchByPlaceId( placeId: string, - options?: searchByPlaceIdOptions + options?: searchByPlaceIdOptions, ): Promise { const credentialsOK = await this._ensureCredentials(); if (!credentialsOK) { @@ -306,7 +303,6 @@ export class AmazonLocationServiceProvider implements GeoProvider { if (place) { return camelcaseKeys(place, { deep: true }) as unknown as Place; } - return; } /** @@ -317,7 +313,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { */ public async searchByCoordinates( coordinates: Coordinates, - options?: SearchByCoordinatesOptions + options?: SearchByCoordinatesOptions, ): Promise { const credentialsOK = await this._ensureCredentials(); if (!credentialsOK) { @@ -344,7 +340,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { customUserAgent: getGeoUserAgent(GeoAction.SearchByCoordinates), }); const command = new SearchPlaceIndexForPositionCommand( - locationServiceInput + locationServiceInput, ); let response; @@ -378,7 +374,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { */ public async saveGeofences( geofences: GeofenceInput[], - options?: AmazonLocationServiceGeofenceOptions + options?: AmazonLocationServiceGeofenceOptions, ): Promise { if (geofences.length < 1) { throw new Error('Geofence input array is empty'); @@ -408,7 +404,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { Polygon: polygon, }, }; - } + }, ); const results: SaveGeofencesResults = { successes: [], @@ -430,7 +426,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { try { response = await this._AmazonLocationServiceBatchPutGeofenceCall( batch, - options?.collectionName || this._config.geofenceCollections.default + options?.collectionName || this._config.geofenceCollections.default, ); } catch (error) { // If the API call fails, add the geofences to the errors array and move to next batch @@ -443,14 +439,15 @@ export class AmazonLocationServiceProvider implements GeoProvider { }, }); }); + return; } // Push all successes to results response.Successes?.forEach(success => { - const { GeofenceId, CreateTime, UpdateTime } = success; + const { GeofenceId: geofenceId, CreateTime, UpdateTime } = success; results.successes.push({ - geofenceId: GeofenceId!, + geofenceId: geofenceId!, createTime: CreateTime, updateTime: UpdateTime, }); @@ -458,17 +455,17 @@ export class AmazonLocationServiceProvider implements GeoProvider { // Push all errors to results response.Errors?.forEach(error => { - const { Error, GeofenceId } = error; + const { Error, GeofenceId: geofenceId } = error; const { Code, Message } = Error!; results.errors.push({ error: { code: Code!, message: Message!, }, - geofenceId: GeofenceId!, + geofenceId: geofenceId!, }); }); - }) + }), ); return results; @@ -482,7 +479,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { */ public async getGeofence( geofenceId: GeofenceId, - options?: AmazonLocationServiceGeofenceOptions + options?: AmazonLocationServiceGeofenceOptions, ): Promise { const credentialsOK = await this._ensureCredentials(); if (!credentialsOK) { @@ -524,10 +521,16 @@ export class AmazonLocationServiceProvider implements GeoProvider { } // Convert response to camelCase for return - const { GeofenceId, CreateTime, UpdateTime, Status, Geometry } = response; + const { + GeofenceId: responseGeofenceId, + CreateTime, + UpdateTime, + Status, + Geometry, + } = response; const geofence: AmazonLocationServiceGeofence = { createTime: CreateTime, - geofenceId: GeofenceId!, + geofenceId: responseGeofenceId!, geometry: { polygon: Geometry!.Polygon as GeofencePolygon, }, @@ -546,7 +549,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { * nextToken: token for next page of geofences */ public async listGeofences( - options?: AmazonLocationServiceListGeofenceOptions + options?: AmazonLocationServiceListGeofenceOptions, ): Promise { const credentialsOK = await this._ensureCredentials(); if (!credentialsOK) { @@ -577,7 +580,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { // Create Amazon Location Service command const command: ListGeofencesCommand = new ListGeofencesCommand( - listGeofencesInput + listGeofencesInput, ); // Make API call @@ -594,9 +597,15 @@ export class AmazonLocationServiceProvider implements GeoProvider { const results: ListGeofenceResults = { entries: Entries!.map( - ({ GeofenceId, CreateTime, UpdateTime, Status, Geometry }) => { + ({ + GeofenceId: entryGeofenceId, + CreateTime, + UpdateTime, + Status, + Geometry, + }) => { return { - geofenceId: GeofenceId!, + geofenceId: entryGeofenceId!, createTime: CreateTime, updateTime: UpdateTime, status: Status, @@ -604,7 +613,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { polygon: Geometry!.Polygon as GeofencePolygon, }, }; - } + }, ), nextToken: NextToken, }; @@ -622,7 +631,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { */ public async deleteGeofences( geofenceIds: string[], - options?: AmazonLocationServiceGeofenceOptions + options?: AmazonLocationServiceGeofenceOptions, ): Promise { if (geofenceIds.length < 1) { throw new Error('GeofenceId input array is empty'); @@ -642,6 +651,8 @@ export class AmazonLocationServiceProvider implements GeoProvider { } catch (error) { return true; } + + return false; }); if (badGeofenceIds.length > 0) { throw new Error(`Invalid geofence ids: ${badGeofenceIds.join(', ')}`); @@ -665,7 +676,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { try { response = await this._AmazonLocationServiceBatchDeleteGeofenceCall( batch, - options?.collectionName || this._config.geofenceCollections.default + options?.collectionName || this._config.geofenceCollections.default, ); } catch (error) { // If the API call fails, add the geofences to the errors array and move to next batch @@ -681,17 +692,19 @@ export class AmazonLocationServiceProvider implements GeoProvider { }; results.errors.push(errorObject); }); + return; } - const badGeofenceIds = response.Errors.map( - ({ geofenceId }) => geofenceId + const badGeofenceIdsFromResponseError = response.Errors.map( + ({ geofenceId }) => geofenceId, ); results.successes.push( - ...batch.filter(Id => !badGeofenceIds.includes(Id)) + ...batch.filter(Id => !badGeofenceIdsFromResponseError.includes(Id)), ); - }) + }), ); + return results; } @@ -700,16 +713,18 @@ export class AmazonLocationServiceProvider implements GeoProvider { */ private async _ensureCredentials(): Promise { try { - const credentials = (await fetchAuthSession()).credentials; + const { credentials } = await fetchAuthSession(); if (!credentials) return false; logger.debug( 'Set credentials for storage. Credentials are:', - credentials + credentials, ); this._credentials = credentials; + return true; } catch (error) { logger.debug('Ensure credentials error. Credentials are:', error); + return false; } } @@ -769,7 +784,7 @@ export class AmazonLocationServiceProvider implements GeoProvider { private async _AmazonLocationServiceBatchPutGeofenceCall( PascalGeofences: BatchPutGeofenceRequestEntry[], - collectionName?: string + collectionName?: string, ) { // Create the BatchPutGeofence input const geofenceInput: BatchPutGeofenceCommandInput = { @@ -785,18 +800,12 @@ export class AmazonLocationServiceProvider implements GeoProvider { }); const command = new BatchPutGeofenceCommand(geofenceInput); - let response: BatchPutGeofenceCommandOutput; - try { - response = await client.send(command); - } catch (error) { - throw error; - } - return response; + return client.send(command); } private async _AmazonLocationServiceBatchDeleteGeofenceCall( geofenceIds: string[], - collectionName?: string + collectionName?: string, ): Promise { // Create the BatchDeleteGeofence input const deleteGeofencesInput: BatchDeleteGeofenceCommandInput = { @@ -812,12 +821,6 @@ export class AmazonLocationServiceProvider implements GeoProvider { }); const command = new BatchDeleteGeofenceCommand(deleteGeofencesInput); - let response: BatchDeleteGeofenceCommandOutput; - try { - response = await client.send(command); - } catch (error) { - throw error; - } - return response; + return client.send(command); } } diff --git a/packages/geo/src/types/AmazonLocationServiceProvider.ts b/packages/geo/src/types/AmazonLocationServiceProvider.ts index dbb9ab7feda..0bf47510ebf 100644 --- a/packages/geo/src/types/AmazonLocationServiceProvider.ts +++ b/packages/geo/src/types/AmazonLocationServiceProvider.ts @@ -1,12 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - MapStyle, - GeofenceOptions, - ListGeofenceOptions, - Geofence, DeleteGeofencesResults, + Geofence, GeofenceError, + GeofenceOptions, + ListGeofenceOptions, + MapStyle, } from './Geo'; // Maps diff --git a/packages/geo/src/types/Geo.ts b/packages/geo/src/types/Geo.ts index 63e4e9014d4..5419ef11b86 100644 --- a/packages/geo/src/types/Geo.ts +++ b/packages/geo/src/types/Geo.ts @@ -53,20 +53,20 @@ export type SearchByTextOptions = | SearchByTextOptionsWithSearchAreaConstraints; // Options object for searchByCoordinates -export type SearchByCoordinatesOptions = { +export interface SearchByCoordinatesOptions { maxResults?: number; searchIndexName?: string; providerName?: string; -}; +} -export type searchByPlaceIdOptions = { +export interface searchByPlaceIdOptions { searchIndexName?: string; -}; +} // Geometry object for Place points -export type PlaceGeometry = { +export interface PlaceGeometry { point: Coordinates; -}; +} // Place object with locality information export interface Place { @@ -88,38 +88,38 @@ export type LinearRing = Coordinates[]; export type GeofencePolygon = LinearRing[]; // Geometry object for Polygon -export type PolygonGeometry = { +export interface PolygonGeometry { polygon: GeofencePolygon; -}; +} export type GeofenceId = string; // Geofence object used as input for saveGeofences -export type GeofenceInput = { +export interface GeofenceInput { geofenceId: GeofenceId; geometry: PolygonGeometry; -}; +} // Options object for saveGeofences -export type GeofenceOptions = { +export interface GeofenceOptions { providerName?: string; -}; +} // Error type for errors related to Geofence API calls -export type GeofenceError = { +export interface GeofenceError { error: { code: string; message: string; }; geofenceId: GeofenceId; -}; +} // Base geofence object -type GeofenceBase = { +interface GeofenceBase { geofenceId: GeofenceId; createTime?: Date; updateTime?: Date; -}; +} // Results object for getGeofence export type Geofence = GeofenceBase & { @@ -127,10 +127,10 @@ export type Geofence = GeofenceBase & { }; // Results object for saveGeofences -export type SaveGeofencesResults = { +export interface SaveGeofencesResults { successes: GeofenceBase[]; errors: GeofenceError[]; -}; +} // Options object for listGeofence export type ListGeofenceOptions = GeofenceOptions & { @@ -138,21 +138,21 @@ export type ListGeofenceOptions = GeofenceOptions & { }; // Results options for listGeofence -export type ListGeofenceResults = { +export interface ListGeofenceResults { entries: Geofence[]; nextToken: string | undefined; -}; +} // Results object for deleteGeofence -export type DeleteGeofencesResults = { +export interface DeleteGeofencesResults { successes: GeofenceId[]; errors: GeofenceError[]; -}; +} // Return type for searchForSuggestions export type SearchForSuggestionsResults = SearchForSuggestionsResult[]; -export type SearchForSuggestionsResult = { +export interface SearchForSuggestionsResult { text: string; placeId?: string; -}; +} diff --git a/packages/geo/src/types/Provider.ts b/packages/geo/src/types/Provider.ts index bd9f169b810..238602779a7 100644 --- a/packages/geo/src/types/Provider.ts +++ b/packages/geo/src/types/Provider.ts @@ -1,20 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - SearchByTextOptions, - SearchByCoordinatesOptions, - SearchForSuggestionsResults, Coordinates, - Place, - MapStyle, + DeleteGeofencesResults, Geofence, GeofenceId, GeofenceInput, GeofenceOptions, ListGeofenceOptions, ListGeofenceResults, + MapStyle, + Place, SaveGeofencesResults, - DeleteGeofencesResults, + SearchByCoordinatesOptions, + SearchByTextOptions, + SearchForSuggestionsResults, searchByPlaceIdOptions, } from './Geo'; diff --git a/packages/geo/src/util.ts b/packages/geo/src/util.ts index 0889ab22e7a..650e8db22f8 100644 --- a/packages/geo/src/util.ts +++ b/packages/geo/src/util.ts @@ -10,23 +10,23 @@ import { import { UserAgent } from '@aws-sdk/types'; import { - Longitude, - Latitude, GeofenceId, GeofenceInput, GeofencePolygon, + Latitude, LinearRing, + Longitude, } from './types'; export function validateCoordinates(lng: Longitude, lat: Latitude): void { if (!Number.isFinite(lng) || !Number.isFinite(lat)) { throw new Error(`Invalid coordinates: [${lng},${lat}]`); } - if (lat < -90 || 90 < lat) { + if (lat < -90 || lat > 90) { throw new Error('Latitude must be between -90 and 90 degrees inclusive.'); - } else if (lng < -180 || 180 < lng) { + } else if (lng < -180 || lng > 180) { throw new Error( - 'Longitude must be between -180 and 180 degrees inclusive.' + 'Longitude must be between -180 and 180 degrees inclusive.', ); } } @@ -37,20 +37,20 @@ export function validateGeofenceId(geofenceId: GeofenceId): void { // Check if geofenceId is valid if (!geofenceIdRegex.test(geofenceId)) { throw new Error( - `Invalid geofenceId: '${geofenceId}' - IDs can only contain alphanumeric characters, hyphens, underscores and periods.` + `Invalid geofenceId: '${geofenceId}' - IDs can only contain alphanumeric characters, hyphens, underscores and periods.`, ); } } export function validateLinearRing( linearRing: LinearRing, - geofenceId?: GeofenceId + geofenceId?: GeofenceId, ): void { const errorPrefix = geofenceId ? `${geofenceId}: ` : ''; // Validate LinearRing size, must be at least 4 points if (linearRing.length < 4) { throw new Error( - `${errorPrefix}LinearRing must contain 4 or more coordinates.` + `${errorPrefix}LinearRing must contain 4 or more coordinates.`, ); } @@ -66,8 +66,8 @@ export function validateLinearRing( if (badCoordinates.length > 0) { throw new Error( `${errorPrefix}One or more of the coordinates in the Polygon LinearRing are not valid: ${JSON.stringify( - badCoordinates - )}` + badCoordinates, + )}`, ); } @@ -77,45 +77,45 @@ export function validateLinearRing( if (lngA !== lngB || latA !== latB) { throw new Error( - `${errorPrefix}LinearRing's first and last coordinates are not the same` + `${errorPrefix}LinearRing's first and last coordinates are not the same`, ); } if (booleanClockwise(linearRing)) { throw new Error( - `${errorPrefix}LinearRing coordinates must be wound counterclockwise` + `${errorPrefix}LinearRing coordinates must be wound counterclockwise`, ); } } export function validatePolygon( polygon: GeofencePolygon, - geofenceId?: GeofenceId + geofenceId?: GeofenceId, ): void { const errorPrefix = geofenceId ? `${geofenceId}: ` : ''; if (!Array.isArray(polygon)) { throw new Error( - `${errorPrefix}Polygon is of incorrect structure. It should be an array of LinearRings` + `${errorPrefix}Polygon is of incorrect structure. It should be an array of LinearRings`, ); } if (polygon.length < 1) { throw new Error( - `${errorPrefix}Polygon must have a single LinearRing array.` + `${errorPrefix}Polygon must have a single LinearRing array.`, ); } if (polygon.length > 1) { throw new Error( - `${errorPrefix}Polygon must have a single LinearRing array. Note: We do not currently support polygons with holes, multipolygons, polygons that are wound clockwise, or that cross the antimeridian.` + `${errorPrefix}Polygon must have a single LinearRing array. Note: We do not currently support polygons with holes, multipolygons, polygons that are wound clockwise, or that cross the antimeridian.`, ); } const verticesCount = polygon.reduce( (prev, linearRing) => prev + linearRing.length, - 0 + 0, ); if (verticesCount > 1000) { throw new Error( - `${errorPrefix}Polygon has more than the maximum 1000 vertices.` + `${errorPrefix}Polygon has more than the maximum 1000 vertices.`, ); } polygon.forEach(linearRing => { @@ -161,11 +161,11 @@ export function validateGeofencesInput(geofences: GeofenceInput[]) { } catch (error) { if ( (error as Error).message.includes( - 'Polygon has more than the maximum 1000 vertices.' + 'Polygon has more than the maximum 1000 vertices.', ) ) { throw new Error( - `Geofence '${geofenceId}' has more than the maximum of 1000 vertices` + `Geofence '${geofenceId}' has more than the maximum of 1000 vertices`, ); } } @@ -186,17 +186,18 @@ export function mapSearchOptions(options, locationServiceInput) { locationServiceModifiedInput.IndexName = options.searchIndexName; } - if (options['biasPosition'] && options['searchAreaConstraints']) { + if (options.biasPosition && options.searchAreaConstraints) { throw new Error( - 'BiasPosition and SearchAreaConstraints are mutually exclusive, please remove one or the other from the options object' + 'BiasPosition and SearchAreaConstraints are mutually exclusive, please remove one or the other from the options object', ); } - if (options['biasPosition']) { - locationServiceModifiedInput.BiasPosition = options['biasPosition']; + if (options.biasPosition) { + locationServiceModifiedInput.BiasPosition = options.biasPosition; } - if (options['searchAreaConstraints']) { - locationServiceModifiedInput.FilterBBox = options['searchAreaConstraints']; + if (options.searchAreaConstraints) { + locationServiceModifiedInput.FilterBBox = options.searchAreaConstraints; } + return locationServiceModifiedInput; } diff --git a/packages/geo/tsconfig.build.json b/packages/geo/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/geo/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/geo/tsconfig.json b/packages/geo/tsconfig.json index 3c7d5bf3f32..311609ce395 100644 --- a/packages/geo/tsconfig.json +++ b/packages/geo/tsconfig.json @@ -1,10 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "strict": true, - "importHelpers": true, - "typeRoots": ["./node_modules/@types", "../../node_modules/@types"], - "types": ["node", "jest"] + "noImplicitAny": false, + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["lib*/**/*.ts", "src"] + "include": ["./src", "__tests__"] } diff --git a/packages/geo/tsconfig.test.json b/packages/geo/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/geo/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/geo/tsconfig.watch.json b/packages/geo/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/geo/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/geo/tslint.json b/packages/geo/tslint.json deleted file mode 100644 index 8eafab1d2b4..00000000000 --- a/packages/geo/tslint.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/interactions/.eslintrc.js b/packages/interactions/.eslintrc.js new file mode 100644 index 00000000000..5f3259efd4d --- /dev/null +++ b/packages/interactions/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/interactions', + }, + ], + }, +}; diff --git a/packages/interactions/__tests__/lex-v1/AWSLexProvider.test.ts b/packages/interactions/__tests__/lex-v1/AWSLexProvider.test.ts index 051c2aa8029..aaca3c0c1d0 100644 --- a/packages/interactions/__tests__/lex-v1/AWSLexProvider.test.ts +++ b/packages/interactions/__tests__/lex-v1/AWSLexProvider.test.ts @@ -255,8 +255,8 @@ describe('Interactions', () => { mockFetchAuthSession.mockReturnValue(Promise.reject(new Error())); await expect( - provider.sendMessage(botConfig.BookTrip, 'hi') - ).rejects.toEqual('No credentials'); + provider.sendMessage(botConfig.BookTrip, 'hi'), + ).rejects.toThrow('No credentials'); expect.assertions(1); }); @@ -268,8 +268,8 @@ describe('Interactions', () => { options: { messageType: 'text', }, - }) - ).rejects.toEqual('invalid content type'); + }), + ).rejects.toThrow('invalid content type'); // obj voice in wrong format await expect( @@ -278,8 +278,8 @@ describe('Interactions', () => { options: { messageType: 'voice', }, - }) - ).rejects.toEqual('invalid content type'); + }), + ).rejects.toThrow('invalid content type'); }); }); @@ -299,7 +299,7 @@ describe('Interactions', () => { test('Configure onComplete callback for a configured bot successfully', () => { expect(() => - provider.onComplete(botConfig.BookTrip, callback) + provider.onComplete(botConfig.BookTrip, callback), ).not.toThrow(); expect.assertions(1); }); @@ -324,7 +324,7 @@ describe('Interactions', () => { // mock callbacks inProgressCallback = jest.fn((err, confirmation) => - fail(`callback shouldn't be called`) + fail(`callback shouldn't be called`), ); completeSuccessCallback = jest.fn((err, confirmation) => { @@ -340,23 +340,23 @@ describe('Interactions', () => { }); completeFailCallback = jest.fn((err, confirmation) => - expect(err).toEqual(new Error('Bot conversation failed')) + expect(err).toEqual(new Error('Bot conversation failed')), ); // mock responses inProgressResp = (await provider.sendMessage( botConfig.BookTrip, - 'hi' + 'hi', )) as PostTextCommandOutput; completeSuccessResp = (await provider.sendMessage( botConfig.BookTrip, - 'done' + 'done', )) as PostTextCommandOutput; completeFailResp = (await provider.sendMessage( botConfig.BookTrip, - 'error' + 'error', )) as PostTextCommandOutput; }); diff --git a/packages/interactions/__tests__/lex-v2/AWSLexV2Provider.test.ts b/packages/interactions/__tests__/lex-v2/AWSLexV2Provider.test.ts index 4f5c526edd7..486e04541a3 100644 --- a/packages/interactions/__tests__/lex-v2/AWSLexV2Provider.test.ts +++ b/packages/interactions/__tests__/lex-v2/AWSLexV2Provider.test.ts @@ -342,8 +342,8 @@ describe('Interactions', () => { mockFetchAuthSession.mockReturnValue(Promise.reject(new Error())); await expect( - provider.sendMessage(botConfig.BookTrip, 'hi') - ).rejects.toEqual('No credentials'); + provider.sendMessage(botConfig.BookTrip, 'hi'), + ).rejects.toThrow('No credentials'); expect.assertions(1); }); @@ -355,8 +355,8 @@ describe('Interactions', () => { options: { messageType: 'text', }, - }) - ).rejects.toEqual('invalid content type'); + }), + ).rejects.toThrow('invalid content type'); // obj voice in wrong format await expect( @@ -365,8 +365,8 @@ describe('Interactions', () => { options: { messageType: 'voice', }, - }) - ).rejects.toEqual('invalid content type'); + }), + ).rejects.toThrow('invalid content type'); }); }); @@ -384,7 +384,7 @@ describe('Interactions', () => { test('Configure onComplete callback for a configured bot successfully', () => { expect(() => - provider.onComplete(botConfig.BookTrip, callback) + provider.onComplete(botConfig.BookTrip, callback), ).not.toThrow(); expect.assertions(1); }); @@ -410,7 +410,7 @@ describe('Interactions', () => { switch (actionType) { case ACTION_TYPE.IN_PROGRESS: return jest.fn((err, confirmation) => - fail(`callback shouldn't be called`) + fail(`callback shouldn't be called`), ); case ACTION_TYPE.COMPLETE: @@ -428,24 +428,24 @@ describe('Interactions', () => { }); case ACTION_TYPE.ERROR: return jest.fn((err, confirmation) => - expect(err).toEqual(new Error('Bot conversation failed')) + expect(err).toEqual(new Error('Bot conversation failed')), ); } }; const inProgressResp = (await provider.sendMessage( botConfig.BookTrip, - 'in progress. callback isnt fired' + 'in progress. callback isnt fired', )) as RecognizeTextCommandOutput; const completeSuccessResp = (await provider.sendMessage( botConfig.BookTrip, - 'done' + 'done', )) as RecognizeTextCommandOutput; const completeFailResp = (await provider.sendMessage( botConfig.BookTrip, - 'error' + 'error', )) as RecognizeTextCommandOutput; mockResponseProvider = actionType => { @@ -469,13 +469,13 @@ describe('Interactions', () => { // callback is only called once conversation is completed let config = { ...botConfig.BookTrip, name: uuid() }; const inProgressCallback = mockCallbackProvider( - ACTION_TYPE.IN_PROGRESS + ACTION_TYPE.IN_PROGRESS, ); provider.onComplete(config, inProgressCallback); provider._reportBotStatus( mockResponseProvider(ACTION_TYPE.IN_PROGRESS), - config + config, ); jest.runAllTimers(); @@ -486,13 +486,13 @@ describe('Interactions', () => { test(`task complete; callback with success resp`, async () => { let config = { ...botConfig.BookTrip, name: uuid() }; const completeSuccessCallback = mockCallbackProvider( - ACTION_TYPE.COMPLETE + ACTION_TYPE.COMPLETE, ); provider.onComplete(config, completeSuccessCallback); provider._reportBotStatus( mockResponseProvider(ACTION_TYPE.COMPLETE), - config + config, ); jest.runAllTimers(); @@ -508,7 +508,7 @@ describe('Interactions', () => { provider._reportBotStatus( mockResponseProvider(ACTION_TYPE.ERROR), - config + config, ); jest.runAllTimers(); diff --git a/packages/interactions/jest.config.js b/packages/interactions/jest.config.js deleted file mode 100644 index 73540b7bc7e..00000000000 --- a/packages/interactions/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 78, - functions: 94, - lines: 96, - statements: 96, - }, - }, - moduleNameMapper: { - uuid: require.resolve('uuid'), - }, -}; diff --git a/packages/interactions/jest.config.mjs b/packages/interactions/jest.config.mjs new file mode 100644 index 00000000000..f000f1fba02 --- /dev/null +++ b/packages/interactions/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 78, + functions: 94, + lines: 96, + statements: 96, + }, + }, + moduleNameMapper: { + uuid: requireResolve('uuid'), + }, +}); + +export default jestConfig; diff --git a/packages/interactions/package.json b/packages/interactions/package.json index 492efd0f3f0..45a36b8c09c 100644 --- a/packages/interactions/package.json +++ b/packages/interactions/package.json @@ -11,19 +11,20 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm run clean && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 88.6" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 88.6" }, "typesVersions": { ">=4.2": { @@ -83,8 +84,7 @@ "devDependencies": { "@aws-amplify/core": "6.0.8", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "^5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/interactions/src/errors/assertValidationError.ts b/packages/interactions/src/errors/assertValidationError.ts index 6870363498c..74b05eb8056 100644 --- a/packages/interactions/src/errors/assertValidationError.ts +++ b/packages/interactions/src/errors/assertValidationError.ts @@ -10,7 +10,7 @@ import { InteractionsError } from './InteractionsError'; export function assertValidationError( assertion: boolean, name: InteractionsValidationErrorCode, - message?: string + message?: string, ): asserts assertion { if (!assertion) { const { message: defaultMessage, recoverySuggestion } = diff --git a/packages/interactions/src/lex-v1/AWSLexProvider.ts b/packages/interactions/src/lex-v1/AWSLexProvider.ts index ba876c5477c..bb182b2718f 100644 --- a/packages/interactions/src/lex-v1/AWSLexProvider.ts +++ b/packages/interactions/src/lex-v1/AWSLexProvider.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - InteractionsOnCompleteCallback, InteractionsMessage, + InteractionsOnCompleteCallback, InteractionsResponse, -} from '../types/Interactions'; +} from '~/src/types/Interactions'; import { DialogState, LexRuntimeServiceClient, @@ -17,7 +17,8 @@ import { } from '@aws-sdk/client-lex-runtime-service'; import { getAmplifyUserAgentObject } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger, fetchAuthSession } from '@aws-amplify/core'; -import { convert } from '../utils'; +import { convert } from '~/src/utils'; + import { AWSLexProviderOption } from './types'; const logger = new ConsoleLogger('AWSLexProvider'); @@ -44,7 +45,7 @@ class AWSLexProvider { */ reportBotStatus( data: AWSLexProviderSendResponse, - { name }: AWSLexProviderOption + { name }: AWSLexProviderOption, ) { const callback = this._botsCompleteCallback[name]; if (!callback) { @@ -68,14 +69,14 @@ class AWSLexProvider { async sendMessage( botConfig: AWSLexProviderOption, - message: string | InteractionsMessage + message: string | InteractionsMessage, ): Promise { // check if credentials are present let session; try { session = await fetchAuthSession(); } catch (error) { - return Promise.reject('No credentials'); + return Promise.reject(new Error('No credentials')); } const { name, region, alias } = botConfig; @@ -100,6 +101,7 @@ class AWSLexProvider { const data = await client.send(postTextCommand); this.reportBotStatus(data, botConfig); + return data; } catch (err) { return Promise.reject(err); @@ -111,7 +113,7 @@ class AWSLexProvider { } = message; if (messageType === 'voice') { if (typeof content !== 'object') { - return Promise.reject('invalid content type'); + return Promise.reject(new Error('invalid content type')); } const inputStream = content instanceof Uint8Array ? content : await convert(content); @@ -126,7 +128,7 @@ class AWSLexProvider { }; } else { if (typeof content !== 'string') - return Promise.reject('invalid content type'); + return Promise.reject(new Error('invalid content type')); params = { botAlias: alias, @@ -149,6 +151,7 @@ class AWSLexProvider { const response = { ...data, ...{ audioStream: audioArray } }; this.reportBotStatus(response, botConfig); + return response; } catch (err) { return Promise.reject(err); @@ -158,7 +161,7 @@ class AWSLexProvider { onComplete( { name }: AWSLexProviderOption, - callback: InteractionsOnCompleteCallback + callback: InteractionsOnCompleteCallback, ) { this._botsCompleteCallback[name] = callback; } diff --git a/packages/interactions/src/lex-v1/apis/onComplete.ts b/packages/interactions/src/lex-v1/apis/onComplete.ts index bee86a26850..1e1bfe10117 100644 --- a/packages/interactions/src/lex-v1/apis/onComplete.ts +++ b/packages/interactions/src/lex-v1/apis/onComplete.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { OnCompleteInput } from '../types'; -import { resolveBotConfig } from '../utils'; -import { lexProvider } from '../AWSLexProvider'; +import { OnCompleteInput } from '~/src/lex-v1/types'; +import { resolveBotConfig } from '~/src/lex-v1/utils'; +import { lexProvider } from '~/src/lex-v1/AWSLexProvider'; import { - assertValidationError, InteractionsValidationErrorCode, -} from '../../errors'; + assertValidationError, +} from '~/src/errors'; export const onComplete = (input: OnCompleteInput): void => { const { botName, callback } = input; @@ -15,7 +15,7 @@ export const onComplete = (input: OnCompleteInput): void => { assertValidationError( !!botConfig, InteractionsValidationErrorCode.NoBotConfig, - `Bot ${botName} does not exist.` + `Bot ${botName} does not exist.`, ); lexProvider.onComplete(botConfig, callback); }; diff --git a/packages/interactions/src/lex-v1/apis/send.ts b/packages/interactions/src/lex-v1/apis/send.ts index 720bb04b040..7624de88f33 100644 --- a/packages/interactions/src/lex-v1/apis/send.ts +++ b/packages/interactions/src/lex-v1/apis/send.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SendInput, SendOutput } from '../types'; -import { resolveBotConfig } from '../utils'; -import { lexProvider } from '../AWSLexProvider'; +import { SendInput, SendOutput } from '~/src/lex-v1/types'; +import { resolveBotConfig } from '~/src/lex-v1/utils'; +import { lexProvider } from '~/src/lex-v1/AWSLexProvider'; import { - assertValidationError, InteractionsValidationErrorCode, -} from '../../errors'; + assertValidationError, +} from '~/src/errors'; export const send = async (input: SendInput): Promise => { const { botName, message } = input; @@ -15,7 +15,8 @@ export const send = async (input: SendInput): Promise => { assertValidationError( !!botConfig, InteractionsValidationErrorCode.NoBotConfig, - `Bot ${botName} does not exist.` + `Bot ${botName} does not exist.`, ); + return lexProvider.sendMessage(botConfig, message); }; diff --git a/packages/interactions/src/lex-v1/index.ts b/packages/interactions/src/lex-v1/index.ts index 38b52ae0637..d88c7ae84e2 100644 --- a/packages/interactions/src/lex-v1/index.ts +++ b/packages/interactions/src/lex-v1/index.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { send, onComplete } from './apis'; +import { onComplete, send } from './apis'; import { IInteractions } from './types/AWSLexProviderOption'; /** diff --git a/packages/interactions/src/lex-v1/types/AWSLexProviderOption.ts b/packages/interactions/src/lex-v1/types/AWSLexProviderOption.ts index 90da3351a21..b6c883374ad 100644 --- a/packages/interactions/src/lex-v1/types/AWSLexProviderOption.ts +++ b/packages/interactions/src/lex-v1/types/AWSLexProviderOption.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SendInput, OnCompleteInput } from './inputs'; +import { OnCompleteInput, SendInput } from './inputs'; import { SendOutput } from './outputs'; export interface AWSLexProviderOption { diff --git a/packages/interactions/src/lex-v1/types/inputs.ts b/packages/interactions/src/lex-v1/types/inputs.ts index 4354941b99e..4436aa8efcf 100644 --- a/packages/interactions/src/lex-v1/types/inputs.ts +++ b/packages/interactions/src/lex-v1/types/inputs.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { - InteractionsSendInput, InteractionsOnCompleteInput, -} from '../../types'; + InteractionsSendInput, +} from '~/src/types'; /** * Input type for LexV1 send API. diff --git a/packages/interactions/src/lex-v1/types/outputs.ts b/packages/interactions/src/lex-v1/types/outputs.ts index e96c8384b8d..c6ee559f198 100644 --- a/packages/interactions/src/lex-v1/types/outputs.ts +++ b/packages/interactions/src/lex-v1/types/outputs.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InteractionsSendOutput } from '../../types'; +import { InteractionsSendOutput } from '~/src/types'; /** * Output type for LexV1 send API. diff --git a/packages/interactions/src/lex-v1/utils/resolveBotConfig.ts b/packages/interactions/src/lex-v1/utils/resolveBotConfig.ts index 2a60b3d4309..fab98818217 100644 --- a/packages/interactions/src/lex-v1/utils/resolveBotConfig.ts +++ b/packages/interactions/src/lex-v1/utils/resolveBotConfig.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AWSLexProviderOption } from '../types'; +import { AWSLexProviderOption } from '~/src/lex-v1/types'; import { Amplify } from '@aws-amplify/core'; export const resolveBotConfig = ( - botName: string + botName: string, ): AWSLexProviderOption | undefined => { const { [botName]: botConfig = undefined } = Amplify.getConfig().Interactions?.LexV1 ?? {}; diff --git a/packages/interactions/src/lex-v2/AWSLexV2Provider.ts b/packages/interactions/src/lex-v2/AWSLexV2Provider.ts index 9f8fab1c2bf..46993c511b2 100644 --- a/packages/interactions/src/lex-v2/AWSLexV2Provider.ts +++ b/packages/interactions/src/lex-v2/AWSLexV2Provider.ts @@ -1,10 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - InteractionsOnCompleteCallback, InteractionsMessage, + InteractionsOnCompleteCallback, InteractionsResponse, -} from '../types/Interactions'; +} from '~/src/types/Interactions'; import { IntentState, LexRuntimeV2Client, @@ -17,8 +17,9 @@ import { } from '@aws-sdk/client-lex-runtime-v2'; import { getAmplifyUserAgentObject } from '@aws-amplify/core/internals/utils'; import { ConsoleLogger, fetchAuthSession } from '@aws-amplify/core'; -import { convert, unGzipBase64AsJson } from '../utils'; +import { convert, unGzipBase64AsJson } from '~/src/utils'; import { v4 as uuid } from 'uuid'; + import { AWSLexV2ProviderOption } from './types'; const logger = new ConsoleLogger('AWSLexV2Provider'); @@ -43,18 +44,19 @@ type AWSLexV2ProviderSendResponse = | RecognizeTextCommandOutput | RecognizeUtteranceCommandOutputFormatted; -type lexV2BaseReqParams = { +interface lexV2BaseReqParams { botId: string; botAliasId: string; localeId: string; sessionId: string; -}; +} class AWSLexV2Provider { private readonly _botsCompleteCallback: Record< string, InteractionsOnCompleteCallback > = {}; + private defaultSessionId: string = uuid(); /** @@ -66,14 +68,14 @@ class AWSLexV2Provider { */ public async sendMessage( botConfig: AWSLexV2ProviderOption, - message: string | InteractionsMessage + message: string | InteractionsMessage, ): Promise { // check if credentials are present let session; try { session = await fetchAuthSession(); } catch (error) { - return Promise.reject('No credentials'); + return Promise.reject(new Error('No credentials')); } const { region, aliasId, localeId, botId } = botConfig; @@ -98,16 +100,17 @@ class AWSLexV2Provider { botConfig, message, reqBaseParams, - client + client, ); } else { response = await this._handleRecognizeUtteranceCommand( botConfig, message, reqBaseParams, - client + client, ); } + return response; } @@ -119,7 +122,7 @@ class AWSLexV2Provider { */ public onComplete( { name }: AWSLexV2ProviderOption, - callback: InteractionsOnCompleteCallback + callback: InteractionsOnCompleteCallback, ) { this._botsCompleteCallback[name] = callback; } @@ -129,7 +132,7 @@ class AWSLexV2Provider { */ _reportBotStatus( data: AWSLexV2ProviderSendResponse, - { name }: AWSLexV2ProviderOption + { name }: AWSLexV2ProviderOption, ) { const sessionState = data?.sessionState; @@ -159,7 +162,7 @@ class AWSLexV2Provider { * update audioStream format */ private async _formatUtteranceCommandOutput( - data: RecognizeUtteranceCommandOutput + data: RecognizeUtteranceCommandOutput, ): Promise { return { ...data, @@ -182,7 +185,7 @@ class AWSLexV2Provider { botConfig: AWSLexV2ProviderOption, data: string, baseParams: lexV2BaseReqParams, - client: LexRuntimeV2Client + client: LexRuntimeV2Client, ) { logger.debug('postText to lex2', data); @@ -193,10 +196,11 @@ class AWSLexV2Provider { try { const recognizeTextCommand = new RecognizeTextCommand(params); - const data = await client.send(recognizeTextCommand); + const result = await client.send(recognizeTextCommand); - this._reportBotStatus(data, botConfig); - return data; + this._reportBotStatus(result, botConfig); + + return result; } catch (err) { return Promise.reject(err); } @@ -210,7 +214,7 @@ class AWSLexV2Provider { botConfig: AWSLexV2ProviderOption, data: InteractionsMessage, baseParams: lexV2BaseReqParams, - client: LexRuntimeV2Client + client: LexRuntimeV2Client, ) { const { content, @@ -223,7 +227,7 @@ class AWSLexV2Provider { // prepare params if (messageType === 'voice') { if (typeof content !== 'object') { - return Promise.reject('invalid content type'); + return Promise.reject(new Error('invalid content type')); } const inputStream = @@ -237,7 +241,7 @@ class AWSLexV2Provider { } else { // text input if (typeof content !== 'string') - return Promise.reject('invalid content type'); + return Promise.reject(new Error('invalid content type')); params = { ...baseParams, @@ -249,10 +253,11 @@ class AWSLexV2Provider { // make API call to lex try { const recognizeUtteranceCommand = new RecognizeUtteranceCommand(params); - const data = await client.send(recognizeUtteranceCommand); + const result = await client.send(recognizeUtteranceCommand); - const response = await this._formatUtteranceCommandOutput(data); + const response = await this._formatUtteranceCommandOutput(result); this._reportBotStatus(response, botConfig); + return response; } catch (err) { return Promise.reject(err); diff --git a/packages/interactions/src/lex-v2/apis/onComplete.ts b/packages/interactions/src/lex-v2/apis/onComplete.ts index 9a972ba5fa2..fcfa95e7320 100644 --- a/packages/interactions/src/lex-v2/apis/onComplete.ts +++ b/packages/interactions/src/lex-v2/apis/onComplete.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { OnCompleteInput } from '../types'; -import { resolveBotConfig } from '../utils'; -import { lexProvider } from '../AWSLexV2Provider'; +import { OnCompleteInput } from '~/src/lex-v2/types'; +import { resolveBotConfig } from '~/src/lex-v2/utils'; +import { lexProvider } from '~/src/lex-v2/AWSLexV2Provider'; import { - assertValidationError, InteractionsValidationErrorCode, -} from '../../errors'; + assertValidationError, +} from '~/src/errors'; export const onComplete = (input: OnCompleteInput): void => { const { botName, callback } = input; @@ -15,7 +15,7 @@ export const onComplete = (input: OnCompleteInput): void => { assertValidationError( !!botConfig, InteractionsValidationErrorCode.NoBotConfig, - `Bot ${botName} does not exist.` + `Bot ${botName} does not exist.`, ); lexProvider.onComplete(botConfig, callback); }; diff --git a/packages/interactions/src/lex-v2/apis/send.ts b/packages/interactions/src/lex-v2/apis/send.ts index c04211a4ebd..e93b9b1b373 100644 --- a/packages/interactions/src/lex-v2/apis/send.ts +++ b/packages/interactions/src/lex-v2/apis/send.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SendInput, SendOutput } from '../types'; -import { lexProvider } from '../AWSLexV2Provider'; -import { resolveBotConfig } from '../utils'; +import { SendInput, SendOutput } from '~/src/lex-v2/types'; +import { lexProvider } from '~/src/lex-v2/AWSLexV2Provider'; +import { resolveBotConfig } from '~/src/lex-v2/utils'; import { - assertValidationError, InteractionsValidationErrorCode, -} from '../../errors'; + assertValidationError, +} from '~/src/errors'; export const send = async (input: SendInput): Promise => { const { botName, message } = input; @@ -15,7 +15,8 @@ export const send = async (input: SendInput): Promise => { assertValidationError( !!botConfig, InteractionsValidationErrorCode.NoBotConfig, - `Bot ${botName} does not exist.` + `Bot ${botName} does not exist.`, ); + return lexProvider.sendMessage(botConfig, message); }; diff --git a/packages/interactions/src/lex-v2/index.ts b/packages/interactions/src/lex-v2/index.ts index 481d4ab4a11..e5afa7fde46 100644 --- a/packages/interactions/src/lex-v2/index.ts +++ b/packages/interactions/src/lex-v2/index.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { send, onComplete } from './apis'; +import { onComplete, send } from './apis'; import { IInteractions } from './types/AWSLexV2ProviderOption'; export const Interactions: IInteractions = { diff --git a/packages/interactions/src/lex-v2/types/AWSLexV2ProviderOption.ts b/packages/interactions/src/lex-v2/types/AWSLexV2ProviderOption.ts index 5e561a58a00..4be0f8e00bd 100644 --- a/packages/interactions/src/lex-v2/types/AWSLexV2ProviderOption.ts +++ b/packages/interactions/src/lex-v2/types/AWSLexV2ProviderOption.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { SendInput, OnCompleteInput } from './inputs'; +import { OnCompleteInput, SendInput } from './inputs'; import { SendOutput } from './outputs'; export interface AWSLexV2ProviderOption { diff --git a/packages/interactions/src/lex-v2/types/inputs.ts b/packages/interactions/src/lex-v2/types/inputs.ts index 0f8b52bbd71..727436cff6b 100644 --- a/packages/interactions/src/lex-v2/types/inputs.ts +++ b/packages/interactions/src/lex-v2/types/inputs.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { - InteractionsSendInput, InteractionsOnCompleteInput, -} from '../../types'; + InteractionsSendInput, +} from '~/src/types'; /** * Input type for LexV2 send API. diff --git a/packages/interactions/src/lex-v2/types/outputs.ts b/packages/interactions/src/lex-v2/types/outputs.ts index 9a221941d68..723d5dbcdb6 100644 --- a/packages/interactions/src/lex-v2/types/outputs.ts +++ b/packages/interactions/src/lex-v2/types/outputs.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InteractionsSendOutput } from '../../types'; +import { InteractionsSendOutput } from '~/src/types'; /** * Output type for LexV2 send API. diff --git a/packages/interactions/src/lex-v2/utils/resolveBotConfig.ts b/packages/interactions/src/lex-v2/utils/resolveBotConfig.ts index a99133e62a0..204e84672be 100644 --- a/packages/interactions/src/lex-v2/utils/resolveBotConfig.ts +++ b/packages/interactions/src/lex-v2/utils/resolveBotConfig.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { AWSLexV2ProviderOption } from '../types'; +import { AWSLexV2ProviderOption } from '~/src/lex-v2/types'; import { Amplify } from '@aws-amplify/core'; export const resolveBotConfig = ( - botName: string + botName: string, ): AWSLexV2ProviderOption | undefined => { const { [botName]: botConfig = undefined } = Amplify.getConfig().Interactions?.LexV2 ?? {}; diff --git a/packages/interactions/src/types/Interactions.ts b/packages/interactions/src/types/Interactions.ts index 59da66dc0cc..acb89f538a5 100644 --- a/packages/interactions/src/types/Interactions.ts +++ b/packages/interactions/src/types/Interactions.ts @@ -1,19 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -export type InteractionsTextMessage = { +export interface InteractionsTextMessage { content: string; options: { messageType: 'text'; }; -}; +} -export type InteractionsVoiceMessage = { +export interface InteractionsVoiceMessage { content: object; options: { messageType: 'voice'; }; -}; +} export type InteractionsMessage = | InteractionsTextMessage @@ -24,6 +24,4 @@ export type InteractionsOnCompleteCallback = ( completion?: InteractionsResponse ) => void; -export type InteractionsResponse = { - [key: string]: any; -}; +export type InteractionsResponse = Record; diff --git a/packages/interactions/src/types/inputs.ts b/packages/interactions/src/types/inputs.ts index 2b5254fe1af..66d53234387 100644 --- a/packages/interactions/src/types/inputs.ts +++ b/packages/interactions/src/types/inputs.ts @@ -6,12 +6,12 @@ import { InteractionsOnCompleteCallback, } from './Interactions'; -export type InteractionsSendInput = { +export interface InteractionsSendInput { botName: string; message: string | InteractionsMessage; -}; +} -export type InteractionsOnCompleteInput = { +export interface InteractionsOnCompleteInput { botName: string; callback: InteractionsOnCompleteCallback; -}; +} diff --git a/packages/interactions/src/utils/commonUtils.ts b/packages/interactions/src/utils/commonUtils.ts index 354ca526386..17338fdeee5 100644 --- a/packages/interactions/src/utils/commonUtils.ts +++ b/packages/interactions/src/utils/commonUtils.ts @@ -12,6 +12,8 @@ export const unGzipBase64AsJson = async (gzipBase64: string | undefined) => { return JSON.parse(objString); } catch (error) { - return Promise.reject('unable to decode and decompress ' + error); + return Promise.reject( + new Error('unable to decode and decompress ' + error), + ); } }; diff --git a/packages/interactions/src/utils/utils.native.ts b/packages/interactions/src/utils/utils.native.ts index f37d8947a17..f26c6340f6d 100644 --- a/packages/interactions/src/utils/utils.native.ts +++ b/packages/interactions/src/utils/utils.native.ts @@ -6,10 +6,10 @@ import { ungzip } from 'pako'; export const convert = async (stream: object): Promise => { if (!(stream instanceof Blob)) { - return Promise.reject('Invalid content type'); + return Promise.reject(new Error('Invalid content type')); } - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { try { const fileReaderInstance = new FileReader(); fileReaderInstance.readAsDataURL(stream); @@ -22,25 +22,26 @@ export const convert = async (stream: object): Promise => { resolve(decodedArrayBuffer); }; } catch (error) { - reject('unable to convert blob to arrayBuffer: ' + error); + reject(new Error('unable to convert blob to arrayBuffer: ' + error)); } }); }; export const base64ToArrayBuffer = (base64: string): Uint8Array => { const binaryString: string = decode(base64); + return Uint8Array.from(binaryString, c => c.charCodeAt(0)); }; export const gzipDecompressToString = async ( - data: Uint8Array + data: Uint8Array, ): Promise => { return new Promise((resolve, reject) => { try { const result: string = ungzip(data, { to: 'string' }); resolve(result); } catch (error) { - reject('unable to decompress' + error); + reject(new Error('unable to decompress' + error)); } }); }; diff --git a/packages/interactions/src/utils/utils.ts b/packages/interactions/src/utils/utils.ts index 607e4c30e27..659a7aedb8b 100644 --- a/packages/interactions/src/utils/utils.ts +++ b/packages/interactions/src/utils/utils.ts @@ -9,7 +9,7 @@ export const convert = async (stream: object): Promise => { .arrayBuffer() .then(buffer => new Uint8Array(buffer)); } else { - return Promise.reject('Invalid content type'); + return Promise.reject(new Error('Invalid content type')); } }; @@ -18,9 +18,9 @@ export const base64ToArrayBuffer = (base64: string): Uint8Array => { }; export const gzipDecompressToString = async ( - data: Uint8Array + data: Uint8Array, ): Promise => { - return await new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { gunzip(data, (err, resp) => { if (err) reject(err); else resolve(strFromU8(resp)); diff --git a/packages/interactions/tsconfig.build.json b/packages/interactions/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/interactions/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/interactions/tsconfig.json b/packages/interactions/tsconfig.json index f907c964821..cc4ae13d539 100644 --- a/packages/interactions/tsconfig.json +++ b/packages/interactions/tsconfig.json @@ -1,9 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/interactions/tsconfig.test.json b/packages/interactions/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/interactions/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/interactions/tsconfig.watch.json b/packages/interactions/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/interactions/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/interactions/tslint.json b/packages/interactions/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/interactions/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/notifications/.eslintrc.js b/packages/notifications/.eslintrc.js new file mode 100644 index 00000000000..ecf96e2d9f8 --- /dev/null +++ b/packages/notifications/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/notifications', + }, + ], + }, +}; diff --git a/packages/notifications/jest.config.js b/packages/notifications/jest.config.js deleted file mode 100644 index 94bcc49fb00..00000000000 --- a/packages/notifications/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 74, - functions: 90, - lines: 91, - statements: 92, - }, - }, -}; diff --git a/packages/notifications/jest.config.mjs b/packages/notifications/jest.config.mjs new file mode 100644 index 00000000000..8b7d353adc1 --- /dev/null +++ b/packages/notifications/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 74, + functions: 90, + lines: 91, + statements: 92, + }, + }, +}); + +export default jestConfig; diff --git a/packages/notifications/package.json b/packages/notifications/package.json index effd39a1c75..ecc45c510ee 100644 --- a/packages/notifications/package.json +++ b/packages/notifications/package.json @@ -11,19 +11,20 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:watch": "tslint 'src/**/*.ts' && jest -w 1 --watch", "build-with-test": "npm run clean && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 88.21" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 88.21" }, "typesVersions": { ">=4.2": { @@ -101,7 +102,6 @@ "@aws-amplify/core": "6.0.8", "@aws-amplify/react-native": "1.0.8", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" } } diff --git a/packages/notifications/src/eventListeners/eventListeners.ts b/packages/notifications/src/eventListeners/eventListeners.ts index 5d255f064c7..784b587b569 100644 --- a/packages/notifications/src/eventListeners/eventListeners.ts +++ b/packages/notifications/src/eventListeners/eventListeners.ts @@ -3,7 +3,10 @@ import { EventListener, EventListenerRemover, EventType } from './types'; -const eventListeners: Record>> = {}; +const eventListeners: Record< + string, + Set unknown>> +> = {}; export const notifyEventListeners = (type: EventType, ...args: any[]): void => { eventListeners[type]?.forEach(listener => { @@ -17,17 +20,15 @@ export const notifyEventListenersAndAwaitHandlers = ( ): Promise => Promise.all( Array.from(eventListeners[type] ?? []).map(async listener => { - try { - await listener.handleEvent(...args); - } catch (err) { - throw err; - } - }) + await listener.handleEvent(...args); + }), ); -export const addEventListener = ( +export const addEventListener = < + EventHandler extends (...args: any[]) => unknown, +>( type: EventType, - handler: EventHandler + handler: EventHandler, ): EventListenerRemover => { // If there is no listener set for the event type, just create it if (!eventListeners[type]) { @@ -40,7 +41,10 @@ export const addEventListener = ( }, }; eventListeners[type].add(listener); + return { - remove: () => listener.remove(), + remove: () => { + listener.remove(); + }, }; }; diff --git a/packages/notifications/src/eventListeners/types.ts b/packages/notifications/src/eventListeners/types.ts index 7c44d928eda..c6af75a09b1 100644 --- a/packages/notifications/src/eventListeners/types.ts +++ b/packages/notifications/src/eventListeners/types.ts @@ -1,16 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InAppMessageInteractionEvent } from '../inAppMessaging/types'; -import { PushNotificationEvent } from '../pushNotifications/types'; +import { InAppMessageInteractionEvent } from '~/src/inAppMessaging/types'; +import { PushNotificationEvent } from '~/src/pushNotifications/types'; -export interface EventListener { +export interface EventListener< + EventHandler extends (...args: any[]) => unknown, +> { handleEvent: EventHandler; - remove: () => void; + remove(): void; } export type EventType = InAppMessageInteractionEvent | PushNotificationEvent; -export type EventListenerRemover = { - remove: () => void; -}; +export interface EventListenerRemover { + remove(): void; +} diff --git a/packages/notifications/src/inAppMessaging/errors/assertServiceError.ts b/packages/notifications/src/inAppMessaging/errors/assertServiceError.ts index 9665304dd01..19fe0bc4c37 100644 --- a/packages/notifications/src/inAppMessaging/errors/assertServiceError.ts +++ b/packages/notifications/src/inAppMessaging/errors/assertServiceError.ts @@ -5,10 +5,11 @@ import { AmplifyErrorCode, ServiceError, } from '@aws-amplify/core/internals/utils'; + import { InAppMessagingError } from './InAppMessagingError'; export function assertServiceError( - error: unknown + error: unknown, ): asserts error is ServiceError { if ( !error || diff --git a/packages/notifications/src/inAppMessaging/errors/assertValidationError.ts b/packages/notifications/src/inAppMessaging/errors/assertValidationError.ts index b9eb9b304d4..d279970e63b 100644 --- a/packages/notifications/src/inAppMessaging/errors/assertValidationError.ts +++ b/packages/notifications/src/inAppMessaging/errors/assertValidationError.ts @@ -12,7 +12,7 @@ import { */ export function assertValidationError( assertion: boolean, - name: InAppMessagingValidationErrorCode + name: InAppMessagingValidationErrorCode, ): asserts assertion { const { message, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/clearMessages.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/clearMessages.ts index ad60bc84e0f..bc00632ebd2 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/clearMessages.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/clearMessages.ts @@ -2,10 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { defaultStorage } from '@aws-amplify/core'; -import { STORAGE_KEY_SUFFIX, PINPOINT_KEY_PREFIX } from '../utils'; - -import { InAppMessagingValidationErrorCode } from '../../../errors'; -import { assertIsInitialized } from '../../../utils'; +import { + PINPOINT_KEY_PREFIX, + STORAGE_KEY_SUFFIX, +} from '~/src/inAppMessaging/providers/pinpoint/utils'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; /** * Clear locally cached messages. @@ -22,5 +24,6 @@ import { assertIsInitialized } from '../../../utils'; export async function clearMessages(): Promise { assertIsInitialized(); const key = `${PINPOINT_KEY_PREFIX}${STORAGE_KEY_SUFFIX}`; - return await defaultStorage.removeItem(key); + + await defaultStorage.removeItem(key); } diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/dispatchEvent.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/dispatchEvent.ts index a371217c767..f6aebe80809 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/dispatchEvent.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/dispatchEvent.ts @@ -5,16 +5,20 @@ import { PINPOINT_KEY_PREFIX, STORAGE_KEY_SUFFIX, processInAppMessages, -} from '../utils'; -import { InAppMessage } from '../../../types'; +} from '~/src/inAppMessaging/providers/pinpoint/utils'; +import { InAppMessage } from '~/src/inAppMessaging/types'; import flatten from 'lodash/flatten.js'; import { defaultStorage } from '@aws-amplify/core'; -import { notifyEventListeners } from '../../../../eventListeners'; -import { assertServiceError } from '../../../errors'; -import { DispatchEventInput } from '../types'; +import { notifyEventListeners } from '~/src/eventListeners'; +import { + InAppMessagingValidationErrorCode, + assertServiceError, +} from '~/src/inAppMessaging/errors'; +import { DispatchEventInput } from '~/src/inAppMessaging/providers/pinpoint/types'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; + import { syncMessages } from './syncMessages'; import { conflictHandler, setConflictHandler } from './setConflictHandler'; -import { assertIsInitialized } from '../../../utils'; /** * Triggers an In-App message to be displayed. Use this after your campaigns have been synced to the device using @@ -47,13 +51,13 @@ export async function dispatchEvent(input: DispatchEventInput): Promise { const cachedMessages = await defaultStorage.getItem(key); const messages: InAppMessage[] = await processInAppMessages( cachedMessages ? JSON.parse(cachedMessages) : [], - input + input, ); const flattenedMessages = flatten(messages); if (flattenedMessages.length > 0) { notifyEventListeners( 'messageReceived', - conflictHandler(flattenedMessages) + conflictHandler(flattenedMessages), ); } } catch (error) { diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/identifyUser.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/identifyUser.ts index 25d59ba0f7b..4fa90f54ebd 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/identifyUser.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/identifyUser.ts @@ -3,26 +3,26 @@ import { InAppMessagingAction } from '@aws-amplify/core/internals/utils'; import { - updateEndpoint, UpdateEndpointException, + updateEndpoint, } from '@aws-amplify/core/internals/providers/pinpoint'; -import { InAppMessagingValidationErrorCode } from '../../../errors'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; import { CATEGORY, CHANNEL_TYPE, getInAppMessagingUserAgentString, resolveConfig, resolveCredentials, -} from '../utils'; -import { IdentifyUserInput } from '../types'; -import { assertIsInitialized } from '../../../utils'; +} from '~/src/inAppMessaging/providers/pinpoint/utils'; +import { IdentifyUserInput } from '~/src/inAppMessaging/providers/pinpoint/types'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; /** * Sends information about a user to Pinpoint. Sending user information allows you to associate a user to their user * profile and activities or actions in your application. Activity can be tracked across devices & platforms by using * the same `userId`. * - * @param {IdentifyUserParameters} params The input object used to construct requests sent to Pinpoint's UpdateEndpoint + * @param {IdentifyUserInput} params The input object used to construct requests sent to Pinpoint's UpdateEndpoint * API. * @throws service: {@link UpdateEndpointException} - Thrown when the underlying Pinpoint service returns an error. * @throws validation: {@link InAppMessagingValidationErrorCode} - Thrown when the provided parameters or library @@ -88,7 +88,7 @@ export const identifyUser = async ({ userId, userProfile, userAgentValue: getInAppMessagingUserAgentString( - InAppMessagingAction.IdentifyUser + InAppMessagingAction.IdentifyUser, ), }); }; diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/initializeInAppMessaging.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/initializeInAppMessaging.ts index 3329623ea3e..2f04dae5245 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/initializeInAppMessaging.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/initializeInAppMessaging.ts @@ -2,14 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { sessionListener } from '@aws-amplify/core/internals/utils'; -import { InAppMessage, InAppMessagingEvent } from '../../../types'; -import { addEventListener } from '../../../../eventListeners'; -import { recordAnalyticsEvent } from '../utils/helpers'; -import { PinpointMessageEvent } from '../types'; +import { InAppMessage, InAppMessagingEvent } from '~/src/inAppMessaging/types'; +import { addEventListener } from '~/src/eventListeners'; +import { recordAnalyticsEvent } from '~/src/inAppMessaging/providers/pinpoint/utils/helpers'; +import { PinpointMessageEvent } from '~/src/inAppMessaging/providers/pinpoint/types'; import { Hub, HubCapsule } from '@aws-amplify/core'; +import { + incrementMessageCounts, + sessionStateChangeHandler, +} from '~/src/inAppMessaging/providers/pinpoint/utils'; +import { initialize, isInitialized } from '~/src/inAppMessaging/utils'; + import { dispatchEvent } from './dispatchEvent'; -import { incrementMessageCounts, sessionStateChangeHandler } from '../utils'; -import { isInitialized, initialize } from '../../../utils'; /** * Initialize and set up in-app messaging category. This API needs to be called to enable other InAppMessaging APIs. diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/notifyMessageInteraction.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/notifyMessageInteraction.ts index fb7da9a1fff..55f8c445756 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/notifyMessageInteraction.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/notifyMessageInteraction.ts @@ -1,9 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { notifyEventListeners } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../utils'; -import { NotifyMessageInteractionInput } from '../types/inputs'; +import { notifyEventListeners } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; +import { NotifyMessageInteractionInput } from '~/src/inAppMessaging/providers/pinpoint/types/inputs'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; /** * Notifies the respective listener of the specified type with the message given. diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageActionTaken.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageActionTaken.ts index f7e3969cb6d..4d0629a3765 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageActionTaken.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageActionTaken.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../utils'; -import { OnMessageActionTakenInput } from '../types/inputs'; -import { OnMessageActionTakenOutput } from '../types/outputs'; +import { addEventListener } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; +import { OnMessageActionTakenInput } from '~/src/inAppMessaging/providers/pinpoint/types/inputs'; +import { OnMessageActionTakenOutput } from '~/src/inAppMessaging/providers/pinpoint/types/outputs'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; /** * Registers a callback that will be invoked on `messageActionTaken` events. @@ -22,8 +23,9 @@ import { OnMessageActionTakenOutput } from '../types/outputs'; * ``` */ export function onMessageActionTaken( - input: OnMessageActionTakenInput + input: OnMessageActionTakenInput, ): OnMessageActionTakenOutput { assertIsInitialized(); + return addEventListener('messageActionTaken', input); } diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDismissed.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDismissed.ts index 6ac6dc4ef83..ba4f7fc99c5 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDismissed.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDismissed.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { OnMessageDismissedOutput } from '../types/outputs'; -import { OnMessageDismissedInput } from '../types/inputs'; -import { assertIsInitialized } from '../../../utils'; +import { addEventListener } from '~/src/eventListeners'; +import { OnMessageDismissedOutput } from '~/src/inAppMessaging/providers/pinpoint/types/outputs'; +import { OnMessageDismissedInput } from '~/src/inAppMessaging/providers/pinpoint/types/inputs'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; /** * Registers a callback that will be invoked on `messageDismissed` events. @@ -22,8 +23,9 @@ import { assertIsInitialized } from '../../../utils'; * ``` */ export function onMessageDismissed( - input: OnMessageDismissedInput + input: OnMessageDismissedInput, ): OnMessageDismissedOutput { assertIsInitialized(); + return addEventListener('messageDismissed', input); } diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDisplayed.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDisplayed.ts index ee672f1a900..97118a9d6a5 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDisplayed.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageDisplayed.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { OnMessageDisplayedOutput } from '../types/outputs'; -import { OnMessageDisplayedInput } from '../types/inputs'; -import { assertIsInitialized } from '../../../utils'; +import { addEventListener } from '~/src/eventListeners'; +import { OnMessageDisplayedOutput } from '~/src/inAppMessaging/providers/pinpoint/types/outputs'; +import { OnMessageDisplayedInput } from '~/src/inAppMessaging/providers/pinpoint/types/inputs'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; /** * Registers a callback that will be invoked on `messageDisplayed` events. @@ -12,7 +13,7 @@ import { assertIsInitialized } from '../../../utils'; * @param {OnMessageDisplayedInput} input - The input object that holds the callback handler. * @throws validation: {@link InAppMessagingValidationErrorCode} - Thrown when the provided parameters or library * configuration is incorrect, or if In App messaging hasn't been initialized. - * @returns {OnMessageDismissedOutput} - An object that holds a remove method to stop listening to events. + * @returns {OnMessageDisplayedOutput} - An object that holds a remove method to stop listening to events. * @example * ```ts * onMessageDisplayed((message) => { @@ -22,8 +23,9 @@ import { assertIsInitialized } from '../../../utils'; * ``` */ export function onMessageDisplayed( - input: OnMessageDisplayedInput + input: OnMessageDisplayedInput, ): OnMessageDisplayedOutput { assertIsInitialized(); + return addEventListener('messageDisplayed', input); } diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageReceived.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageReceived.ts index a80e42af6e5..ffbbc73c6f6 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageReceived.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/onMessageReceived.ts @@ -1,10 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../utils'; -import { OnMessageReceivedInput } from '../types/inputs'; -import { OnMessageReceivedOutput } from '../types/outputs'; +import { addEventListener } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; +import { OnMessageReceivedInput } from '~/src/inAppMessaging/providers/pinpoint/types/inputs'; +import { OnMessageReceivedOutput } from '~/src/inAppMessaging/providers/pinpoint/types/outputs'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; /** * Registers a callback that will be invoked on `messageReceived` events. @@ -22,8 +23,9 @@ import { OnMessageReceivedOutput } from '../types/outputs'; * ``` */ export function onMessageReceived( - input: OnMessageReceivedInput + input: OnMessageReceivedInput, ): OnMessageReceivedOutput { assertIsInitialized(); + return addEventListener('messageReceived', input); } diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/setConflictHandler.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/setConflictHandler.ts index 58333a11c9d..e71d2581b2e 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/setConflictHandler.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/setConflictHandler.ts @@ -1,13 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InAppMessage } from '../../../types'; -import { assertIsInitialized } from '../../../utils'; -import { InAppMessageConflictHandler, SetConflictHandlerInput } from '../types'; +import { InAppMessage } from '~/src/inAppMessaging/types'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; +import { + InAppMessageConflictHandler, + SetConflictHandlerInput, +} from '~/src/inAppMessaging/providers/pinpoint/types'; +import { InAppMessagingValidationErrorCode } from '~/src/inAppMessaging/errors'; +// TODO(eslint): remove this linter suppression with refactoring. +// eslint-disable-next-line import/no-mutable-exports export let conflictHandler: InAppMessageConflictHandler = defaultConflictHandler; - /** * Set a conflict handler that will be used to resolve conflicts that may emerge * when matching events with synced messages. @@ -60,9 +65,11 @@ function defaultConflictHandler(messages: InAppMessage[]): InAppMessage { if (!endDateA && endDateB) { return 1; } + // otherwise, compare them return new Date(endDateA) < new Date(endDateB) ? -1 : 1; }); + // always return the top sorted return sorted[0]; } diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/syncMessages.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/syncMessages.ts index 58e6def29e6..87a90756473 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/syncMessages.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/apis/syncMessages.ts @@ -5,24 +5,24 @@ import { InAppMessagingAction } from '@aws-amplify/core/internals/utils'; import { resolveEndpointId } from '@aws-amplify/core/internals/providers/pinpoint'; import { defaultStorage } from '@aws-amplify/core'; import { - resolveConfig, - resolveCredentials, - getInAppMessagingUserAgentString, - STORAGE_KEY_SUFFIX, - PINPOINT_KEY_PREFIX, CATEGORY, CHANNEL_TYPE, -} from '../utils'; + PINPOINT_KEY_PREFIX, + STORAGE_KEY_SUFFIX, + getInAppMessagingUserAgentString, + resolveConfig, + resolveCredentials, +} from '~/src/inAppMessaging/providers/pinpoint/utils'; import { - getInAppMessages, GetInAppMessagesInput, GetInAppMessagesOutput, + getInAppMessages, } from '@aws-amplify/core/internals/aws-clients/pinpoint'; import { InAppMessagingValidationErrorCode, assertServiceError, -} from '../../../errors'; -import { assertIsInitialized } from '../../../utils'; +} from '~/src/inAppMessaging/errors'; +import { assertIsInitialized } from '~/src/inAppMessaging/utils'; /** * Fetch and persist messages from Pinpoint campaigns. @@ -66,7 +66,7 @@ async function fetchInAppMessages() { identityId, region, userAgentValue: getInAppMessagingUserAgentString( - InAppMessagingAction.SyncMessages + InAppMessagingAction.SyncMessages, ), }); @@ -76,10 +76,11 @@ async function fetchInAppMessages() { }; const response: GetInAppMessagesOutput = await getInAppMessages( { credentials, region }, - input + input, ); const { InAppMessageCampaigns: messages } = response.InAppMessagesResponse ?? {}; + return messages; } catch (error) { assertServiceError(error); diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/types/inputs.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/types/inputs.ts index 092bdd88b91..ffe560b0b1e 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/types/inputs.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/types/inputs.ts @@ -1,17 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { IdentifyUserOptions } from './options'; -import { - InAppMessageConflictHandler, - OnMessageInteractionEventHandler, -} from './types'; import { InAppMessage, InAppMessageInteractionEvent, InAppMessagingEvent, InAppMessagingIdentifyUserInput, -} from '../../../types'; +} from '~/src/inAppMessaging/types'; + +import { IdentifyUserOptions } from './options'; +import { + InAppMessageConflictHandler, + OnMessageInteractionEventHandler, +} from './types'; /** * Input type for Pinpoint identifyUser API. @@ -52,7 +53,7 @@ export type OnMessageActionTakenInput = OnMessageInteractionEventHandler; /** * Input type for NotifyMessageInteraction API. */ -export type NotifyMessageInteractionInput = { +export interface NotifyMessageInteractionInput { message: InAppMessage; type: InAppMessageInteractionEvent; -}; +} diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/types/outputs.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/types/outputs.ts index 11a438ba0dc..0dddecb17d2 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/types/outputs.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/types/outputs.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventListenerRemover } from '../../../../eventListeners'; +import { EventListenerRemover } from '~/src/eventListeners'; /** * Output type for OnMessageReceived API. diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/types/types.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/types/types.ts index 89185f8fb87..b8071127c9e 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/types/types.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/types/types.ts @@ -1,24 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InAppMessage } from '../../../types'; +import { InAppMessage } from '~/src/inAppMessaging/types'; export type InAppMessageCountMap = Record; -export type DailyInAppMessageCounter = { +export interface DailyInAppMessageCounter { count: number; lastCountTimestamp: string; -}; +} -export type InAppMessageCounts = { +export interface InAppMessageCounts { sessionCount: number; dailyCount: number; totalCount: number; -}; +} export type MetricsComparator = ( metricsVal: number, - eventVal: number + eventVal: number, ) => boolean; export enum PinpointMessageEvent { @@ -28,7 +28,7 @@ export enum PinpointMessageEvent { } export type InAppMessageConflictHandler = ( - messages: InAppMessage[] + messages: InAppMessage[], ) => InAppMessage; export type OnMessageInteractionEventHandler = (message: InAppMessage) => void; diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/helpers.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/helpers.ts index be0a1bb5b24..06c5d3fb388 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/helpers.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/helpers.ts @@ -3,7 +3,6 @@ import { ConsoleLogger } from '@aws-amplify/core'; import { InAppMessagingAction } from '@aws-amplify/core/internals/utils'; -import type { InAppMessageCampaign as PinpointInAppMessage } from '@aws-amplify/core/internals/aws-clients/pinpoint'; import isEmpty from 'lodash/isEmpty.js'; import { InAppMessage, @@ -12,14 +11,20 @@ import { InAppMessageLayout, InAppMessageTextAlign, InAppMessagingEvent, -} from '../../../types'; -import { MetricsComparator, PinpointMessageEvent } from '../types'; +} from '~/src/inAppMessaging/types'; +import { + MetricsComparator, + PinpointMessageEvent, +} from '~/src/inAppMessaging/providers/pinpoint/types'; import { record as recordCore } from '@aws-amplify/core/internals/providers/pinpoint'; + import { resolveConfig } from './resolveConfig'; import { resolveCredentials } from './resolveCredentials'; import { CATEGORY } from './constants'; import { getInAppMessagingUserAgentString } from './userAgent'; +import type { InAppMessageCampaign as PinpointInAppMessage } from '@aws-amplify/core/internals/aws-clients/pinpoint'; + const DELIVERY_TYPE = 'IN_APP_MESSAGE'; let eventNameMemo: Record = {}; @@ -30,7 +35,7 @@ export const logger = new ConsoleLogger('InAppMessaging.Pinpoint.Utils'); export const recordAnalyticsEvent = ( event: PinpointMessageEvent, - message: InAppMessage + message: InAppMessage, ) => { const { appId, region } = resolveConfig(); @@ -53,7 +58,7 @@ export const recordAnalyticsEvent = ( identityId, region, userAgentValue: getInAppMessagingUserAgentString( - InAppMessagingAction.NotifyMessageInteraction + InAppMessagingAction.NotifyMessageInteraction, ), }); }) @@ -66,24 +71,26 @@ export const recordAnalyticsEvent = ( export const getStartOfDay = (): string => { const now = new Date(); now.setHours(0, 0, 0, 0); + return now.toISOString(); }; export const matchesEventType = ( { CampaignId, Schedule }: PinpointInAppMessage, - { name: eventType }: InAppMessagingEvent + { name: eventType }: InAppMessagingEvent, ) => { const { EventType } = Schedule?.EventFilter?.Dimensions ?? {}; const memoKey = `${CampaignId}:${eventType}`; - if (!eventNameMemo.hasOwnProperty(memoKey)) { + if (!Object.prototype.hasOwnProperty.call(eventNameMemo, memoKey)) { eventNameMemo[memoKey] = !!EventType?.Values?.includes(eventType); } + return eventNameMemo[memoKey]; }; export const matchesAttributes = ( { CampaignId, Schedule }: PinpointInAppMessage, - { attributes = {} }: InAppMessagingEvent + { attributes = {} }: InAppMessagingEvent, ): boolean => { const { Attributes } = Schedule?.EventFilter?.Dimensions ?? {}; if (isEmpty(Attributes)) { @@ -95,19 +102,20 @@ export const matchesAttributes = ( return false; } const memoKey = `${CampaignId}:${JSON.stringify(attributes)}`; - if (!eventAttributesMemo.hasOwnProperty(memoKey)) { + if (!Object.prototype.hasOwnProperty.call(eventAttributesMemo, memoKey)) { eventAttributesMemo[memoKey] = !Attributes || Object.entries(Attributes).every( - ([key, { Values }]) => Values?.includes(attributes[key]) + ([key, { Values }]) => Values?.includes(attributes[key]), ); } + return eventAttributesMemo[memoKey]; }; export const matchesMetrics = ( { CampaignId, Schedule }: PinpointInAppMessage, - { metrics = {} }: InAppMessagingEvent + { metrics = {} }: InAppMessagingEvent, ): boolean => { const { Metrics } = Schedule?.EventFilter?.Dimensions ?? {}; if (isEmpty(Metrics)) { @@ -119,20 +127,22 @@ export const matchesMetrics = ( return false; } const memoKey = `${CampaignId}:${JSON.stringify(metrics)}`; - if (!eventMetricsMemo.hasOwnProperty(memoKey)) { + if (!Object.prototype.hasOwnProperty.call(eventMetricsMemo, memoKey)) { eventMetricsMemo[memoKey] = !Metrics || Object.entries(Metrics).every(([key, { ComparisonOperator, Value }]) => { const compare = getComparator(ComparisonOperator); + // if there is some unknown comparison operator, treat as a comparison failure return compare && !!Value ? compare(Value, metrics[key]) : false; }); } + return eventMetricsMemo[memoKey]; }; export const getComparator = ( - operator?: string + operator?: string, ): MetricsComparator | undefined => { switch (operator) { case 'EQUAL': @@ -156,6 +166,7 @@ export const isBeforeEndDate = ({ if (!Schedule?.EndDate) { return true; } + return new Date() < new Date(Schedule.EndDate); }; @@ -187,13 +198,13 @@ export const isQuietTime = (message: PinpointInAppMessage): boolean => { Number.parseInt(startHours, 10), Number.parseInt(startMinutes, 10), 0, - 0 + 0, ); end.setHours( Number.parseInt(endHours, 10), Number.parseInt(endMinutes, 10), 0, - 0 + 0, ); // if quiet time includes midnight, bump the end time to the next day @@ -201,11 +212,12 @@ export const isQuietTime = (message: PinpointInAppMessage): boolean => { end.setDate(end.getDate() + 1); } - const isQuietTime = now >= start && now <= end; - if (isQuietTime) { + const result = now >= start && now <= end; + if (result) { logger.debug('message filtered due to quiet time', message); } - return isQuietTime; + + return result; }; export const clearMemo = () => { @@ -225,7 +237,7 @@ export const clearMemo = () => { // - 2. Amplify correctly handles the legacy layout values from Pinpoint after they are updated export const interpretLayout = ( - layout: NonNullable['Layout'] + layout: NonNullable['Layout'], ): InAppMessageLayout => { if (layout === 'MOBILE_FEED') { return 'MODAL'; @@ -315,18 +327,19 @@ export const extractContent = ({ }, }; } + return extractedContent; }) ?? [] ); }; export const extractMetadata = ({ - InAppMessage, + InAppMessage: InAppMessagePayload, Priority, Schedule, TreatmentId, }: PinpointInAppMessage): InAppMessage['metadata'] => ({ - customData: InAppMessage?.CustomConfig, + customData: InAppMessagePayload?.CustomConfig, endDate: Schedule?.EndDate, priority: Priority, treatmentId: TreatmentId, diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/messageProcessingHelpers.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/messageProcessingHelpers.ts index fe22b139b87..9ef62c6611d 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/messageProcessingHelpers.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/messageProcessingHelpers.ts @@ -1,12 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { InAppMessage, InAppMessagingEvent } from '../../../types'; +import { InAppMessage, InAppMessagingEvent } from '~/src/inAppMessaging/types'; import { - InAppMessageCounts, - InAppMessageCountMap, DailyInAppMessageCounter, -} from '../types'; + InAppMessageCountMap, + InAppMessageCounts, +} from '~/src/inAppMessaging/providers/pinpoint/types'; +import { ConsoleLogger, defaultStorage } from '@aws-amplify/core'; +import { SessionState } from '@aws-amplify/core/internals/utils'; + import { extractContent, extractMetadata, @@ -16,9 +19,8 @@ import { matchesEventType, matchesMetrics, } from './helpers'; + import type { InAppMessageCampaign as PinpointInAppMessage } from '@aws-amplify/core/internals/aws-clients/pinpoint'; -import { defaultStorage, ConsoleLogger } from '@aws-amplify/core'; -import { SessionState } from '@aws-amplify/core/internals/utils'; const MESSAGE_DAILY_COUNT_KEY = 'pinpointProvider_inAppMessages_dailyCount'; const MESSAGE_TOTAL_COUNT_KEY = 'pinpointProvider_inAppMessages_totalCount'; @@ -28,12 +30,11 @@ let sessionMessageCountMap: InAppMessageCountMap = {}; export async function processInAppMessages( messages: PinpointInAppMessage[], - event: InAppMessagingEvent + event: InAppMessagingEvent, ): Promise { let highestPrioritySeen: number | undefined; let acc: PinpointInAppMessage[] = []; - for (let index = 0; index < messages.length; index++) { - const message = messages[index]; + for (const message of messages) { const messageQualifies = matchesEventType(message, event) && matchesAttributes(message, event) && @@ -65,6 +66,7 @@ export async function processInAppMessages( } } } + return normalizeMessages(acc); } @@ -85,14 +87,15 @@ export async function incrementMessageCounts(messageId: string): Promise { function normalizeMessages(messages: PinpointInAppMessage[]): InAppMessage[] { return messages.map(message => { - const { CampaignId, InAppMessage } = message; + const { CampaignId, InAppMessage: InAppMessagePayload } = message; + return { // Default to empty string in rare cases we don't have a campaignId id: CampaignId ?? '', content: extractContent(message), // Default to TOP_BANNER layout in rare cases we don't have a Layout - layout: InAppMessage?.Layout - ? interpretLayout(InAppMessage.Layout) + layout: InAppMessagePayload?.Layout + ? interpretLayout(InAppMessagePayload.Layout) : 'TOP_BANNER', metadata: extractMetadata(message), }; @@ -104,7 +107,7 @@ async function isBelowCap({ SessionCap, DailyCap, TotalCap, -}: PinpointInAppMessage): Promise { +}: PinpointInAppMessage): Promise { const { sessionCount, dailyCount, totalCount } = await getMessageCounts(CampaignId); @@ -116,7 +119,7 @@ async function isBelowCap({ } async function getMessageCounts( - messageId?: string + messageId?: string, ): Promise { let messageCounts = { sessionCount: 0, @@ -131,6 +134,7 @@ async function getMessageCounts( dailyCount: await getDailyCount(), totalCount: await getTotalCount(messageId), }; + return messageCounts; } catch (err) { logger.error('Failed to get message counts from storage', err); @@ -185,18 +189,21 @@ async function getDailyCount(): Promise { const counter: DailyInAppMessageCounter = item ? JSON.parse(item) : { count: 0, lastCountTimestamp: today }; + // If the stored counter timestamp is today, use it as the count, otherwise reset to 0 return counter.lastCountTimestamp === today ? counter.count : 0; } async function getTotalCountMap(): Promise { const item = await defaultStorage.getItem(MESSAGE_TOTAL_COUNT_KEY); + // Parse stored count map or initialize as empty return item ? JSON.parse(item) : {}; } async function getTotalCount(messageId: string): Promise { const countMap = await getTotalCountMap(); + // Return stored count or initialize as empty count return countMap[messageId] || 0; } @@ -204,5 +211,6 @@ async function getTotalCount(messageId: string): Promise { const getStartOfDay = (): string => { const now = new Date(); now.setHours(0, 0, 0, 0); + return now.toISOString(); }; diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveConfig.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveConfig.ts index 6406f8289eb..6bd03f22482 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveConfig.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveConfig.ts @@ -5,7 +5,7 @@ import { Amplify } from '@aws-amplify/core'; import { InAppMessagingValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/inAppMessaging/errors'; /** * @internal @@ -15,5 +15,6 @@ export const resolveConfig = () => { Amplify.getConfig().Notifications?.InAppMessaging?.Pinpoint ?? {}; assertValidationError(!!appId, InAppMessagingValidationErrorCode.NoAppId); assertValidationError(!!region, InAppMessagingValidationErrorCode.NoRegion); + return { appId, region }; }; diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveCredentials.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveCredentials.ts index 90104cb57a7..8abb54f9bf9 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveCredentials.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/resolveCredentials.ts @@ -5,7 +5,7 @@ import { fetchAuthSession } from '@aws-amplify/core'; import { InAppMessagingValidationErrorCode, assertValidationError, -} from '../../../errors'; +} from '~/src/inAppMessaging/errors'; /** * @internal @@ -14,7 +14,8 @@ export const resolveCredentials = async () => { const { credentials, identityId } = await fetchAuthSession(); assertValidationError( !!credentials, - InAppMessagingValidationErrorCode.NoCredentials + InAppMessagingValidationErrorCode.NoCredentials, ); + return { credentials, identityId }; }; diff --git a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/userAgent.ts b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/userAgent.ts index 00f1433dda7..bc00a697ea0 100644 --- a/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/userAgent.ts +++ b/packages/notifications/src/inAppMessaging/providers/pinpoint/utils/userAgent.ts @@ -2,14 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { Category, - getAmplifyUserAgentObject, - getAmplifyUserAgent, InAppMessagingAction, + getAmplifyUserAgent, + getAmplifyUserAgentObject, } from '@aws-amplify/core/internals/utils'; import { UserAgent } from '@aws-sdk/types'; export function getInAppMessagingUserAgent( - action: InAppMessagingAction + action: InAppMessagingAction, ): UserAgent { return getAmplifyUserAgentObject({ category: Category.InAppMessaging, diff --git a/packages/notifications/src/inAppMessaging/types/event.ts b/packages/notifications/src/inAppMessaging/types/event.ts index 2b5d0255c4e..f0107f11d92 100644 --- a/packages/notifications/src/inAppMessaging/types/event.ts +++ b/packages/notifications/src/inAppMessaging/types/event.ts @@ -7,8 +7,8 @@ export type InAppMessageInteractionEvent = | 'messageDismissed' | 'messageActionTaken'; -export type InAppMessagingEvent = { +export interface InAppMessagingEvent { name: string; attributes?: Record; metrics?: Record; -}; +} diff --git a/packages/notifications/src/inAppMessaging/types/inputs.ts b/packages/notifications/src/inAppMessaging/types/inputs.ts index 0df082ea000..e9056602b5b 100644 --- a/packages/notifications/src/inAppMessaging/types/inputs.ts +++ b/packages/notifications/src/inAppMessaging/types/inputs.ts @@ -2,15 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { UserProfile } from '@aws-amplify/core'; + import { InAppMessagingServiceOptions } from './options'; /** * Input type for `identifyUser`. */ -export type InAppMessagingIdentifyUserInput< - ServiceOptions extends - InAppMessagingServiceOptions = InAppMessagingServiceOptions, -> = { +export interface InAppMessagingIdentifyUserInput< + ServiceOptions extends InAppMessagingServiceOptions = InAppMessagingServiceOptions, +> { /** * A User ID associated to the current device. */ @@ -25,4 +25,4 @@ export type InAppMessagingIdentifyUserInput< * Options to be passed to the API. */ options?: ServiceOptions; -}; +} diff --git a/packages/notifications/src/inAppMessaging/types/options.ts b/packages/notifications/src/inAppMessaging/types/options.ts index e7827741aee..29017456286 100644 --- a/packages/notifications/src/inAppMessaging/types/options.ts +++ b/packages/notifications/src/inAppMessaging/types/options.ts @@ -4,4 +4,4 @@ /** * Base type for service options. */ -export type InAppMessagingServiceOptions = Record; +export type InAppMessagingServiceOptions = object; diff --git a/packages/notifications/src/inAppMessaging/utils/statusHelpers.ts b/packages/notifications/src/inAppMessaging/utils/statusHelpers.ts index 8f44c11dec6..ab370520318 100644 --- a/packages/notifications/src/inAppMessaging/utils/statusHelpers.ts +++ b/packages/notifications/src/inAppMessaging/utils/statusHelpers.ts @@ -4,7 +4,7 @@ import { InAppMessagingValidationErrorCode, assertValidationError, -} from '../errors'; +} from '~/src/inAppMessaging/errors'; let initialized = false; @@ -27,6 +27,6 @@ export const isInitialized = () => initialized; export function assertIsInitialized() { assertValidationError( isInitialized(), - InAppMessagingValidationErrorCode.NotInitialized + InAppMessagingValidationErrorCode.NotInitialized, ); } diff --git a/packages/notifications/src/pushNotifications/errors/errorHelpers.ts b/packages/notifications/src/pushNotifications/errors/errorHelpers.ts index 20e54209095..071af48e51f 100644 --- a/packages/notifications/src/pushNotifications/errors/errorHelpers.ts +++ b/packages/notifications/src/pushNotifications/errors/errorHelpers.ts @@ -6,7 +6,8 @@ import { AssertionFunction, createAssertionFunction, } from '@aws-amplify/core/internals/utils'; -import { isInitialized } from '../utils/initializationManager'; +import { isInitialized } from '~/src/pushNotifications/utils/initializationManager'; + import { PushNotificationError } from './PushNotificationError'; export enum PushNotificationValidationErrorCode { @@ -37,7 +38,7 @@ const pushNotificationValidationErrorMap: AmplifyErrorMap = createAssertionFunction( pushNotificationValidationErrorMap, - PushNotificationError + PushNotificationError, ); export const assertIsInitialized = () => { diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.native.ts index e2feeb46137..b78708720ce 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.native.ts @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAmplifyPushNotification } from '@aws-amplify/react-native'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { GetBadgeCount } from '../types'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { GetBadgeCount } from '~/src/pushNotifications/providers/pinpoint/types'; const { getBadgeCount: getBadgeCountNative } = loadAmplifyPushNotification(); export const getBadgeCount: GetBadgeCount = async () => { assertIsInitialized(); + return getBadgeCountNative(); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.ts index 86834063c1b..b10ef6a794b 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getBadgeCount.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { GetBadgeCount } from '../types'; +import { GetBadgeCount } from '~/src/pushNotifications/providers/pinpoint/types'; /** * Returns the current badge count (the number next to your app's icon). This function is safe to call (but will be diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.native.ts index ffecd7ce60e..20905e768bd 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.native.ts @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAmplifyPushNotification } from '@aws-amplify/react-native'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { GetLaunchNotification } from '../types'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { GetLaunchNotification } from '~/src/pushNotifications/providers/pinpoint/types'; const { getLaunchNotification: getLaunchNotificationNative } = loadAmplifyPushNotification(); export const getLaunchNotification: GetLaunchNotification = async () => { assertIsInitialized(); + return getLaunchNotificationNative(); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.ts index fd85039bc2f..61ae132e66a 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getLaunchNotification.ts @@ -2,8 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { PushNotificationMessage } from '../../../types'; -import { GetLaunchNotification, GetLaunchNotificationOutput } from '../types'; +import { PushNotificationMessage } from '~/src/pushNotifications/types'; +import { + GetLaunchNotification, + GetLaunchNotificationOutput, +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Returns the notification which launched your app from a terminated state. The launch notification is consumed by diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.native.ts index 2f11ad58ec2..81324b25cde 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.native.ts @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAmplifyPushNotification } from '@aws-amplify/react-native'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { GetPermissionStatus } from '../types'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { GetPermissionStatus } from '~/src/pushNotifications/providers/pinpoint/types'; const { getPermissionStatus: getPermissionStatusNative } = loadAmplifyPushNotification(); export const getPermissionStatus: GetPermissionStatus = async () => { assertIsInitialized(); + return getPermissionStatusNative(); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.ts index 04c5f5ee758..7eb286a4c96 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/getPermissionStatus.ts @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { GetPermissionStatus, GetPermissionStatusOutput } from '../types'; +import { + GetPermissionStatus, + GetPermissionStatusOutput, +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Returns a string representing the current status of user permissions to display push notifications. The possible diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.native.ts index 79d862ae750..d2c4c08ad6d 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.native.ts @@ -3,13 +3,16 @@ import { PushNotificationAction } from '@aws-amplify/core/internals/utils'; import { updateEndpoint } from '@aws-amplify/core/internals/providers/pinpoint'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; import { getPushNotificationUserAgentString, resolveCredentials, -} from '../../../utils'; -import { getChannelType, resolveConfig } from '../utils'; -import { IdentifyUser } from '../types'; +} from '~/src/pushNotifications/utils'; +import { + getChannelType, + resolveConfig, +} from '~/src/pushNotifications/providers/pinpoint/utils'; +import { IdentifyUser } from '~/src/pushNotifications/providers/pinpoint/types'; export const identifyUser: IdentifyUser = async ({ userId, @@ -33,7 +36,7 @@ export const identifyUser: IdentifyUser = async ({ userId, userProfile, userAgentValue: getPushNotificationUserAgentString( - PushNotificationAction.IdentifyUser + PushNotificationAction.IdentifyUser, ), }); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.ts index 6659a1be7b8..8dc8e462ea4 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/identifyUser.ts @@ -3,8 +3,11 @@ import { UpdateEndpointException } from '@aws-amplify/core/internals/providers/pinpoint'; import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { PushNotificationValidationErrorCode } from '../../../errors'; -import { IdentifyUser, IdentifyUserInput } from '../types'; +import { PushNotificationValidationErrorCode } from '~/src/pushNotifications/errors'; +import { + IdentifyUser, + IdentifyUserInput, +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Sends information about a user to Pinpoint. Sending user information allows you to associate a user to their user diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.native.ts index fda560f8069..0737bc3b510 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.native.ts @@ -10,7 +10,7 @@ import { addEventListener, notifyEventListeners, notifyEventListenersAndAwaitHandlers, -} from '../../../../eventListeners'; +} from '~/src/eventListeners'; import { getPushNotificationUserAgentString, getToken, @@ -18,12 +18,12 @@ import { isInitialized, resolveCredentials, setToken, -} from '../../../utils'; +} from '~/src/pushNotifications/utils'; import { createMessageEventRecorder, getChannelType, resolveConfig, -} from '../utils'; +} from '~/src/pushNotifications/providers/pinpoint/utils'; const { addMessageEventListener, @@ -40,6 +40,7 @@ const BACKGROUND_TASK_TIMEOUT = 25; // seconds export const initializePushNotifications = (): void => { if (isInitialized()) { logger.info('Push notifications have already been enabled'); + return; } addNativeListeners(); @@ -48,9 +49,6 @@ export const initializePushNotifications = (): void => { }; const addNativeListeners = (): void => { - let launchNotificationOpenedListener: ReturnType< - typeof addMessageEventListener - > | null; const { NativeEvent, NativeHeadlessTaskKey } = getConstants(); const { BACKGROUND_MESSAGE_RECEIVED, @@ -66,7 +64,7 @@ const addNativeListeners = (): void => { // keep headless task running until handlers have completed their work await notifyEventListenersAndAwaitHandlers( 'backgroundMessageReceived', - message + message, ); }); } else if (BACKGROUND_MESSAGE_RECEIVED) { @@ -80,19 +78,19 @@ const addNativeListeners = (): void => { await Promise.race([ notifyEventListenersAndAwaitHandlers( 'backgroundMessageReceived', - message + message, ), // background tasks will get suspended and all future tasks be deprioritized by the OS if they run for // more than 30 seconds so we reject with a error in a shorter amount of time to prevent this from // happening - new Promise((_, reject) => { - setTimeout( - () => - reject( - `onNotificationReceivedInBackground handlers should complete their work within ${BACKGROUND_TASK_TIMEOUT} seconds, but they did not.` + new Promise((resolve, reject) => { + setTimeout(() => { + reject( + new Error( + `onNotificationReceivedInBackground handlers should complete their work within ${BACKGROUND_TASK_TIMEOUT} seconds, but they did not.`, ), - BACKGROUND_TASK_TIMEOUT * 1000 - ); + ); + }, BACKGROUND_TASK_TIMEOUT * 1000); }), ]); } catch (err) { @@ -103,7 +101,7 @@ const addNativeListeners = (): void => { completeNotification(completionHandlerId); } } - } + }, ); } @@ -112,9 +110,13 @@ const addNativeListeners = (): void => { FOREGROUND_MESSAGE_RECEIVED, message => { notifyEventListeners('foregroundMessageReceived', message); - } + }, ); + let launchNotificationOpenedListener: ReturnType< + typeof addMessageEventListener + > | null; + launchNotificationOpenedListener = LAUNCH_NOTIFICATION_OPENED ? addMessageEventListener( // listen for native notification opened app (user tapped on notification, opening the app from quit - @@ -125,7 +127,8 @@ const addNativeListeners = (): void => { notifyEventListeners('launchNotificationOpened', message); // once we are done with it we can remove the listener launchNotificationOpenedListener?.remove(); - } + launchNotificationOpenedListener = null; + }, ) : null; @@ -137,7 +140,7 @@ const addNativeListeners = (): void => { notifyEventListeners('notificationOpened', message); // if we are in this state, we no longer need the listener as the app was launched via some other means launchNotificationOpenedListener?.remove(); - } + }, ); addTokenEventListener( @@ -157,7 +160,7 @@ const addNativeListeners = (): void => { logger.error('Failed to register device for push notifications', err); throw err; } - } + }, ); }; @@ -167,27 +170,30 @@ const addAnalyticsListeners = (): void => { // wire up default Pinpoint message event handling addEventListener( 'backgroundMessageReceived', - createMessageEventRecorder('received_background') + createMessageEventRecorder('received_background'), ); addEventListener( 'foregroundMessageReceived', - createMessageEventRecorder('received_foreground') + createMessageEventRecorder('received_foreground'), ); launchNotificationOpenedListenerRemover = addEventListener( 'launchNotificationOpened', createMessageEventRecorder( 'opened_notification', // once we are done with it we can remove the listener - launchNotificationOpenedListenerRemover?.remove - ) + () => { + launchNotificationOpenedListenerRemover?.remove(); + launchNotificationOpenedListenerRemover = undefined; + }, + ), ); addEventListener( 'notificationOpened', createMessageEventRecorder( 'opened_notification', // if we are in this state, we no longer need the listener as the app was launched via some other means - launchNotificationOpenedListenerRemover?.remove - ) + launchNotificationOpenedListenerRemover?.remove, + ), ); }; @@ -203,7 +209,7 @@ const registerDevice = async (address: string): Promise => { channelType: getChannelType(), identityId, userAgentValue: getPushNotificationUserAgentString( - PushNotificationAction.InitializePushNotifications + PushNotificationAction.InitializePushNotifications, ), }); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.ts index 009342a2df9..2a8a957a442 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/initializePushNotifications.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { InitializePushNotifications } from '../types'; +import { InitializePushNotifications } from '~/src/pushNotifications/providers/pinpoint/types'; /** * Initialize and set up the push notification category. The category must be first initialized before all other diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.native.ts index a2694b0a02a..a06446309d1 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.native.ts @@ -1,11 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { OnNotificationOpened } from '../types'; +import { addEventListener } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { OnNotificationOpened } from '~/src/pushNotifications/providers/pinpoint/types'; export const onNotificationOpened: OnNotificationOpened = input => { assertIsInitialized(); + return addEventListener('notificationOpened', input); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.ts index 90570d0a484..4bcf6b7b8cb 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationOpened.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { PushNotificationMessage } from '../../../types'; +import { PushNotificationMessage } from '~/src/pushNotifications/types'; import { OnNotificationOpened, OnNotificationOpenedInput, OnNotificationOpenedOutput, -} from '../types'; +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Registers a listener that will be triggered when a notification is opened by user. diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.native.ts index b43f561ef0e..c173752bef1 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.native.ts @@ -1,12 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { OnNotificationReceivedInBackground } from '../types'; +import { addEventListener } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { OnNotificationReceivedInBackground } from '~/src/pushNotifications/providers/pinpoint/types'; export const onNotificationReceivedInBackground: OnNotificationReceivedInBackground = input => { assertIsInitialized(); + return addEventListener('backgroundMessageReceived', input); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.ts index b47c03eaa36..cb0b8de0a59 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInBackground.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { PushNotificationMessage } from '../../../types'; +import { PushNotificationMessage } from '~/src/pushNotifications/types'; import { OnNotificationReceivedInBackground, OnNotificationReceivedInBackgroundInput, OnNotificationReceivedInBackgroundOutput, -} from '../types'; +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Registers a listener that will be triggered when a notification is received while app is in a background state. diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.native.ts index 6afd65e51bd..05db760963c 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.native.ts @@ -1,12 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { OnNotificationReceivedInForeground } from '../types'; +import { addEventListener } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { OnNotificationReceivedInForeground } from '~/src/pushNotifications/providers/pinpoint/types'; export const onNotificationReceivedInForeground: OnNotificationReceivedInForeground = input => { assertIsInitialized(); + return addEventListener('foregroundMessageReceived', input); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.ts index 1fed662673a..56a33ef6ea5 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onNotificationReceivedInForeground.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { PushNotificationMessage } from '../../../types'; +import { PushNotificationMessage } from '~/src/pushNotifications/types'; import { OnNotificationReceivedInForeground, OnNotificationReceivedInForegroundInput, OnNotificationReceivedInForegroundOutput, -} from '../types'; +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Registers a listener that will be triggered when a notification is received while app is in a foreground state. diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.native.ts index 2d688a438df..7675c2cf6a9 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.native.ts @@ -1,11 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { addEventListener } from '../../../../eventListeners'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { OnTokenReceived } from '../types'; +import { addEventListener } from '~/src/eventListeners'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { OnTokenReceived } from '~/src/pushNotifications/providers/pinpoint/types'; export const onTokenReceived: OnTokenReceived = input => { assertIsInitialized(); + return addEventListener('tokenReceived', input); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.ts index ab28b35392a..54212884032 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/onTokenReceived.ts @@ -6,7 +6,7 @@ import { OnTokenReceived, OnTokenReceivedInput, OnTokenReceivedOutput, -} from '../types'; +} from '~/src/pushNotifications/providers/pinpoint/types'; /** * Registers a listener that will be triggered when a token is received. A token will be received: diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.native.ts index f4a83901562..6eb7fd34f93 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.native.ts @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAmplifyPushNotification } from '@aws-amplify/react-native'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { RequestPermissions } from '../types'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { RequestPermissions } from '~/src/pushNotifications/providers/pinpoint/types'; const { requestPermissions: requestPermissionsNative } = loadAmplifyPushNotification(); export const requestPermissions: RequestPermissions = async input => { assertIsInitialized(); + return requestPermissionsNative(input); }; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.ts index ef9e01d43b3..7a679b40daf 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/requestPermissions.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { RequestPermissions } from '../types'; +import { RequestPermissions } from '~/src/pushNotifications/providers/pinpoint/types'; /** * Requests notification permissions from your user. By default, Amplify requests all supported permissions but you can diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.native.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.native.ts index dd8e3738d20..f9c12405127 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.native.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.native.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { loadAmplifyPushNotification } from '@aws-amplify/react-native'; -import { assertIsInitialized } from '../../../errors/errorHelpers'; -import { SetBadgeCount } from '../types'; +import { assertIsInitialized } from '~/src/pushNotifications/errors/errorHelpers'; +import { SetBadgeCount } from '~/src/pushNotifications/providers/pinpoint/types'; const { setBadgeCount: setBadgeCountNative } = loadAmplifyPushNotification(); diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.ts index 6e0e767c18a..b6913cd61c0 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/apis/setBadgeCount.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; -import { SetBadgeCount } from '../types'; +import { SetBadgeCount } from '~/src/pushNotifications/providers/pinpoint/types'; /** * Sets the current badge count (the number on the top right corner of your app's icon). Setting the badge count diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/types/analytics.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/types/analytics.ts index 26a9617ca80..8ce79f54c96 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/types/analytics.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/types/analytics.ts @@ -8,7 +8,7 @@ export type PinpointMessageEvent = export type PinpointMessageEventSource = '_campaign' | '_journey'; -export type AnalyticsEventAttributes = { +export interface AnalyticsEventAttributes { source: PinpointMessageEventSource; attributes: Record; -}; +} diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/types/inputs.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/types/inputs.ts index 12128c68837..f36f4db933d 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/types/inputs.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/types/inputs.ts @@ -1,16 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { IdentifyUserOptions } from './options'; import { PushNotificationIdentifyUserInput, - PushNotificationRequestPermissionsInput, - PushNotificationSetBadgeCountInput, PushNotificationOnNotificationOpenedInput, PushNotificationOnNotificationReceivedInBackgroundInput, PushNotificationOnNotificationReceivedInForegroundInput, PushNotificationOnTokenReceivedInput, -} from '../../../types'; + PushNotificationRequestPermissionsInput, + PushNotificationSetBadgeCountInput, +} from '~/src/pushNotifications/types'; + +import { IdentifyUserOptions } from './options'; export type IdentifyUserInput = PushNotificationIdentifyUserInput; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/types/outputs.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/types/outputs.ts index a28106178e6..6bc60f8ab6f 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/types/outputs.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/types/outputs.ts @@ -10,7 +10,7 @@ import { PushNotificationOnNotificationReceivedInForegroundOutput, PushNotificationOnTokenReceivedOutput, PushNotificationRequestPermissionsOutput, -} from '../../../types'; +} from '~/src/pushNotifications/types'; export type GetBadgeCountOutput = PushNotificationGetBadgeCountOutput; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/createMessageEventRecorder.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/createMessageEventRecorder.ts index e1d7b242644..a0909e78111 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/createMessageEventRecorder.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/createMessageEventRecorder.ts @@ -4,12 +4,13 @@ import { record } from '@aws-amplify/core/internals/providers/pinpoint'; import { ConsoleLogger } from '@aws-amplify/core'; import { AWSCredentials } from '@aws-amplify/core/internals/utils'; -import { PinpointMessageEvent } from '../types'; +import { PinpointMessageEvent } from '~/src/pushNotifications/providers/pinpoint/types'; import { OnPushNotificationMessageHandler, PushNotificationMessage, -} from '../../../types'; -import { resolveCredentials } from '../../../utils'; +} from '~/src/pushNotifications/types'; +import { resolveCredentials } from '~/src/pushNotifications/utils'; + import { getAnalyticsEvent } from './getAnalyticsEvent'; import { getChannelType } from './getChannelType'; import { resolveConfig } from './resolveConfig'; @@ -22,7 +23,7 @@ const logger = new ConsoleLogger('PushNotification.recordMessageEvent'); export const createMessageEventRecorder = ( event: PinpointMessageEvent, - callback?: Function + callback?: (...args: any[]) => unknown, ): OnPushNotificationMessageHandler => async message => { const { credentials } = await resolveCredentials(); @@ -53,8 +54,10 @@ const recordMessageEvent = async ({ const analyticsEvent = getAnalyticsEvent(message, event); if (!analyticsEvent) { logger.debug('A notification missing event information was not recorded'); + return; } + return record({ appId, category: 'PushNotification', diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getAnalyticsEvent.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getAnalyticsEvent.ts index 86c1e5c8d8a..a4aec3f79df 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getAnalyticsEvent.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getAnalyticsEvent.ts @@ -2,8 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { PinpointAnalyticsEvent } from '@aws-amplify/core/internals/providers/pinpoint'; -import { AnalyticsEventAttributes, PinpointMessageEvent } from '../types'; -import { PushNotificationMessage } from '../../../types'; +import { + AnalyticsEventAttributes, + PinpointMessageEvent, +} from '~/src/pushNotifications/providers/pinpoint/types'; +import { PushNotificationMessage } from '~/src/pushNotifications/types'; const ANDROID_CAMPAIGN_ACTIVITY_ID_KEY = 'pinpoint.campaign.campaign_activity_id'; @@ -15,7 +18,7 @@ const ANDROID_CAMPAIGN_TREATMENT_ID_KEY = 'pinpoint.campaign.treatment_id'; */ export const getAnalyticsEvent = ( { data }: PushNotificationMessage, - event: PinpointMessageEvent + event: PinpointMessageEvent, ): PinpointAnalyticsEvent | null => { if (!data) { return null; @@ -25,6 +28,7 @@ export const getAnalyticsEvent = ( return null; } const { source, attributes } = eventAttributes; + return { attributes, name: `${source}.${event}`, @@ -32,12 +36,12 @@ export const getAnalyticsEvent = ( }; const getAnalyticsEventAttributes = ( - data: PushNotificationMessage['data'] + data: PushNotificationMessage['data'], ): AnalyticsEventAttributes | undefined => { if (!data) { return; } - if (data.hasOwnProperty(ANDROID_CAMPAIGN_ID_KEY)) { + if (Object.prototype.hasOwnProperty.call(data, ANDROID_CAMPAIGN_ID_KEY)) { return { source: '_campaign', attributes: { diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getChannelType.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getChannelType.ts index 728f873c2b6..83a67074569 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getChannelType.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/getChannelType.ts @@ -3,7 +3,7 @@ import { PlatformNotSupportedError } from '@aws-amplify/core/internals/utils'; import { getOperatingSystem } from '@aws-amplify/react-native'; -import { ChannelType } from '../types'; +import { ChannelType } from '~/src/pushNotifications/providers/pinpoint/types'; const operatingSystem = getOperatingSystem(); const isAndroid = operatingSystem === 'android'; diff --git a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/resolveConfig.ts b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/resolveConfig.ts index a6a92beb454..5171948cde4 100644 --- a/packages/notifications/src/pushNotifications/providers/pinpoint/utils/resolveConfig.ts +++ b/packages/notifications/src/pushNotifications/providers/pinpoint/utils/resolveConfig.ts @@ -2,7 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { assert, PushNotificationValidationErrorCode } from '../../../errors'; +import { + PushNotificationValidationErrorCode, + assert, +} from '~/src/pushNotifications/errors'; /** * @internal @@ -12,5 +15,6 @@ export const resolveConfig = () => { Amplify.getConfig().Notifications?.PushNotification?.Pinpoint ?? {}; assert(!!appId, PushNotificationValidationErrorCode.NoAppId); assert(!!region, PushNotificationValidationErrorCode.NoRegion); + return { appId, region }; }; diff --git a/packages/notifications/src/pushNotifications/types/inputs.ts b/packages/notifications/src/pushNotifications/types/inputs.ts index 994c345e658..6650c133dc9 100644 --- a/packages/notifications/src/pushNotifications/types/inputs.ts +++ b/packages/notifications/src/pushNotifications/types/inputs.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { UserProfile } from '@aws-amplify/core'; + import { PushNotificationServiceOptions } from './options'; import { PushNotificationPermissions } from './module'; import { @@ -9,10 +10,9 @@ import { OnTokenReceivedHandler, } from './pushNotifications'; -export type PushNotificationIdentifyUserInput< - ServiceOptions extends - PushNotificationServiceOptions = PushNotificationServiceOptions, -> = { +export interface PushNotificationIdentifyUserInput< + ServiceOptions extends PushNotificationServiceOptions = PushNotificationServiceOptions, +> { /** * A User ID associated to the current device. */ @@ -27,7 +27,7 @@ export type PushNotificationIdentifyUserInput< * Options to be passed to the API. */ options?: ServiceOptions; -}; +} export type PushNotificationRequestPermissionsInput = PushNotificationPermissions; diff --git a/packages/notifications/src/pushNotifications/types/options.ts b/packages/notifications/src/pushNotifications/types/options.ts index 654a0c82be8..82f4f3e0e05 100644 --- a/packages/notifications/src/pushNotifications/types/options.ts +++ b/packages/notifications/src/pushNotifications/types/options.ts @@ -4,4 +4,4 @@ /** * Base type for service options. */ -export type PushNotificationServiceOptions = Record; +export type PushNotificationServiceOptions = object; diff --git a/packages/notifications/src/pushNotifications/types/outputs.ts b/packages/notifications/src/pushNotifications/types/outputs.ts index ce79afc76fc..b16749810f6 100644 --- a/packages/notifications/src/pushNotifications/types/outputs.ts +++ b/packages/notifications/src/pushNotifications/types/outputs.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { EventListenerRemover } from '../../eventListeners'; +import { EventListenerRemover } from '~/src/eventListeners'; import { PushNotificationMessage, PushNotificationPermissionStatus, -} from '../types'; +} from '~/src/pushNotifications/types'; export type PushNotificationGetBadgeCountOutput = number | null; diff --git a/packages/notifications/src/pushNotifications/utils/getPushNotificationUserAgentString.ts b/packages/notifications/src/pushNotifications/utils/getPushNotificationUserAgentString.ts index c374698a9ed..766a1d4f475 100644 --- a/packages/notifications/src/pushNotifications/utils/getPushNotificationUserAgentString.ts +++ b/packages/notifications/src/pushNotifications/utils/getPushNotificationUserAgentString.ts @@ -1,13 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - PushNotificationAction, Category, + PushNotificationAction, getAmplifyUserAgent, } from '@aws-amplify/core/internals/utils'; export const getPushNotificationUserAgentString = ( - action: PushNotificationAction + action: PushNotificationAction, ) => getAmplifyUserAgent({ category: Category.PushNotification, diff --git a/packages/notifications/src/pushNotifications/utils/resolveCredentials.ts b/packages/notifications/src/pushNotifications/utils/resolveCredentials.ts index 9067f56b1b8..29aa139882e 100644 --- a/packages/notifications/src/pushNotifications/utils/resolveCredentials.ts +++ b/packages/notifications/src/pushNotifications/utils/resolveCredentials.ts @@ -3,9 +3,9 @@ import { fetchAuthSession } from '@aws-amplify/core'; import { - assert, PushNotificationValidationErrorCode, -} from '../errors/errorHelpers'; + assert, +} from '~/src/pushNotifications/errors/errorHelpers'; /** * @internal @@ -13,5 +13,6 @@ import { export const resolveCredentials = async () => { const { credentials, identityId } = await fetchAuthSession(); assert(!!credentials, PushNotificationValidationErrorCode.NoCredentials); + return { credentials, identityId }; }; diff --git a/packages/notifications/tsconfig.build.json b/packages/notifications/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/notifications/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/notifications/tsconfig.json b/packages/notifications/tsconfig.json index 3c0bca8b5e2..cfd3c46cdf0 100755 --- a/packages/notifications/tsconfig.json +++ b/packages/notifications/tsconfig.json @@ -1,11 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "target": "esnext", - "lib": ["dom", "es2019", "esnext.asynciterable", "esnext"], + "rootDirs": ["src"], + "baseUrl": ".", "importHelpers": false, - "strict": true, - "noImplicitAny": true + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/notifications/tsconfig.test.json b/packages/notifications/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/notifications/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/notifications/tsconfig.watch.json b/packages/notifications/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/notifications/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/notifications/tslint.json b/packages/notifications/tslint.json deleted file mode 100644 index 8eafab1d2b4..00000000000 --- a/packages/notifications/tslint.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/predictions/.eslintrc.js b/packages/predictions/.eslintrc.js new file mode 100644 index 00000000000..ec249adebe4 --- /dev/null +++ b/packages/predictions/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/predictions', + }, + ], + }, +}; diff --git a/packages/predictions/__tests__/providers/AWSAIIdentifyPredictionsProvider.test.ts b/packages/predictions/__tests__/providers/AWSAIIdentifyPredictionsProvider.test.ts index 5aa2140502f..62be415f130 100644 --- a/packages/predictions/__tests__/providers/AWSAIIdentifyPredictionsProvider.test.ts +++ b/packages/predictions/__tests__/providers/AWSAIIdentifyPredictionsProvider.test.ts @@ -287,20 +287,20 @@ mockGetUrl.mockImplementation(({ key, options }) => { let url: URL; if (level === 'guest') { url = new URL( - `https://bucket-name.s3.us-west-2.amazonaws.com/public/${key}?X-Amz-Algorithm=AWS4-HMAC-SHA256` + `https://bucket-name.s3.us-west-2.amazonaws.com/public/${key}?X-Amz-Algorithm=AWS4-HMAC-SHA256`, ); } else { const identityId = options?.targetIdentityId || 'identityId'; // tslint:disable-next-line: max-line-length url = new URL( - `https://bucket-name.s3.us-west-2.amazonaws.com/${level}/${identityId}/key.png?X-Amz-Algorithm=AWS4-HMAC-SHA256` + `https://bucket-name.s3.us-west-2.amazonaws.com/${level}/${identityId}/key.png?X-Amz-Algorithm=AWS4-HMAC-SHA256`, ); } return Promise.resolve({ url }); }); describe('Predictions identify provider test', () => { - let predictionsProvider; + let predictionsProvider: AmazonAIIdentifyPredictionsProvider; beforeAll(() => { predictionsProvider = new AmazonAIIdentifyPredictionsProvider(); @@ -320,17 +320,19 @@ describe('Predictions identify provider test', () => { }, }; expect( - predictionsProvider.identify(detectTextInput) + predictionsProvider.identify(detectTextInput), ).resolves.toMatchObject(expected); }); test('error case no credentials', () => { mockFetchAuthSession.mockResolvedValueOnce({}); - expect(predictionsProvider.identify(detectTextInput)).rejects.toThrow( + expect(() => + predictionsProvider.identify(detectTextInput), + ).rejects.toThrow( expect.objectContaining( - validationErrorMap[PredictionsValidationErrorCode.NoCredentials] - ) + validationErrorMap[PredictionsValidationErrorCode.NoCredentials], + ), ); }); @@ -387,7 +389,7 @@ describe('Predictions identify provider test', () => { }, }; expect( - predictionsProvider.identify(detectTextInput) + predictionsProvider.identify(detectTextInput), ).resolves.toMatchObject(expected); }); }); @@ -408,7 +410,7 @@ describe('Predictions identify provider test', () => { ], }; expect( - predictionsProvider.identify(detectLabelInput) + predictionsProvider.identify(detectLabelInput), ).resolves.toMatchObject(expected); }); test('error case credentials do not exist', () => { @@ -416,19 +418,19 @@ describe('Predictions identify provider test', () => { expect(predictionsProvider.identify(detectLabelInput)).rejects.toThrow( expect.objectContaining( - validationErrorMap[PredictionsValidationErrorCode.NoCredentials] - ) + validationErrorMap[PredictionsValidationErrorCode.NoCredentials], + ), ); }); test('error case credentials exist but service fails', () => { jest .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(() => { - return Promise.reject('error'); + return Promise.reject(new Error('error')); }); - expect(predictionsProvider.identify(detectLabelInput)).rejects.toMatch( - 'error' - ); + expect(() => + predictionsProvider.identify(detectLabelInput), + ).rejects.toThrow('error'); }); }); @@ -440,7 +442,7 @@ describe('Predictions identify provider test', () => { test('happy case credentials exist, unsafe image', () => { const expected: IdentifyLabelsOutput = { unsafe: 'YES' }; expect( - predictionsProvider.identify(detectModerationInput) + predictionsProvider.identify(detectModerationInput), ).resolves.toMatchObject(expected); }); @@ -448,11 +450,11 @@ describe('Predictions identify provider test', () => { jest .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(() => { - return Promise.reject('error'); + return Promise.reject(new Error('error')); }); - expect( - predictionsProvider.identify(detectModerationInput) - ).rejects.toMatch('error'); + expect(() => + predictionsProvider.identify(detectModerationInput), + ).rejects.toThrow('error'); }); }); @@ -472,7 +474,7 @@ describe('Predictions identify provider test', () => { unsafe: 'YES', }; expect( - predictionsProvider.identify(detectModerationInput) + predictionsProvider.identify(detectModerationInput), ).resolves.toMatchObject(expected); }); @@ -480,11 +482,11 @@ describe('Predictions identify provider test', () => { jest .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(() => { - return Promise.reject('error'); + return Promise.reject(new Error('error')); }); - expect( - predictionsProvider.identify(detectModerationInput) - ).rejects.toMatch('error'); + expect(() => + predictionsProvider.identify(detectModerationInput), + ).rejects.toThrow('error'); }); }); }); @@ -514,7 +516,7 @@ describe('Predictions identify provider test', () => { ], }; expect( - predictionsProvider.identify(detectFacesInput) + predictionsProvider.identify(detectFacesInput), ).resolves.toMatchObject(expected); }); @@ -523,8 +525,8 @@ describe('Predictions identify provider test', () => { expect(predictionsProvider.identify(detectFacesInput)).rejects.toThrow( expect.objectContaining( - validationErrorMap[PredictionsValidationErrorCode.NoCredentials] - ) + validationErrorMap[PredictionsValidationErrorCode.NoCredentials], + ), ); }); @@ -532,10 +534,10 @@ describe('Predictions identify provider test', () => { jest .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(() => { - return Promise.reject('error'); + return Promise.reject(new Error('error')); }); - expect(predictionsProvider.identify(detectFacesInput)).rejects.toMatch( - 'error' + expect(predictionsProvider.identify(detectFacesInput)).rejects.toThrow( + 'error', ); }); }); @@ -553,7 +555,7 @@ describe('Predictions identify provider test', () => { entities: [{ boundingBox: { left: 0, top: 0, height: 0, width: 0 } }], }; expect( - predictionsProvider.identify(recognizeCelebritiesInput) + predictionsProvider.identify(recognizeCelebritiesInput), ).resolves.toMatchObject(expected); }); @@ -561,11 +563,11 @@ describe('Predictions identify provider test', () => { jest .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(() => { - return Promise.reject('error'); + return Promise.reject(new Error('error')); }); expect( - predictionsProvider.identify(recognizeCelebritiesInput) - ).rejects.toMatch('error'); + predictionsProvider.identify(recognizeCelebritiesInput), + ).rejects.toThrow('error'); }); }); @@ -584,7 +586,7 @@ describe('Predictions identify provider test', () => { entities: [{ boundingBox: { left: 0, top: 0, height: 0, width: 0 } }], }; expect( - predictionsProvider.identify(searchByFacesInput) + predictionsProvider.identify(searchByFacesInput), ).resolves.toMatchObject(expected); }); @@ -592,11 +594,11 @@ describe('Predictions identify provider test', () => { jest .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(() => { - return Promise.reject('error'); + return Promise.reject(new Error('error')); }); expect( - predictionsProvider.identify(searchByFacesInput) - ).rejects.toMatch('error'); + predictionsProvider.identify(searchByFacesInput), + ).rejects.toThrow('error'); }); }); }); @@ -622,7 +624,7 @@ describe('Predictions identify provider test', () => { .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(command => { expect( - (command as DetectLabelsCommand).input.Image?.S3Object?.Name + (command as DetectLabelsCommand).input.Image?.S3Object?.Name, ).toMatch('public/key'); return Promise.resolve(detectlabelsResponse); }); @@ -637,7 +639,7 @@ describe('Predictions identify provider test', () => { .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(command => { expect( - (command as DetectLabelsCommand).input.Image?.S3Object?.Name + (command as DetectLabelsCommand).input.Image?.S3Object?.Name, ).toMatch('private/identityId/key'); return {}; }); @@ -659,7 +661,7 @@ describe('Predictions identify provider test', () => { .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(command => { expect( - (command as DetectLabelsCommand).input.Image?.S3Object?.Name + (command as DetectLabelsCommand).input.Image?.S3Object?.Name, ).toMatch('protected/identityId/key'); return Promise.resolve(detectlabelsResponse); }); @@ -674,7 +676,7 @@ describe('Predictions identify provider test', () => { .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(command => { expect((command as DetectLabelsCommand).input.Image?.Bytes).toMatch( - 'bytes' + 'bytes', ); return Promise.resolve(detectlabelsResponse); }); @@ -690,7 +692,7 @@ describe('Predictions identify provider test', () => { .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(command => { expect( - (command as DetectLabelsCommand).input.Image?.Bytes + (command as DetectLabelsCommand).input.Image?.Bytes, ).toStrictEqual(fileInput); return {}; }); @@ -707,7 +709,7 @@ describe('Predictions identify provider test', () => { .spyOn(RekognitionClient.prototype, 'send') .mockImplementationOnce(command => { expect( - (command as DetectLabelsCommand).input.Image?.Bytes + (command as DetectLabelsCommand).input.Image?.Bytes, ).toMatchObject(fileInput); return {}; }); @@ -718,9 +720,9 @@ describe('Predictions identify provider test', () => { const detectLabelInput = { labels: { source: null, type: 'LABELS' }, }; - expect(predictionsProvider.identify(detectLabelInput)).rejects.toMatch( - 'not configured correctly' - ); + expect( + predictionsProvider.identify(detectLabelInput as any), + ).rejects.toThrow('not configured correctly'); }); }); @@ -736,12 +738,12 @@ describe('Predictions identify provider test', () => { await predictionsProvider.identify(detectLabelInput); expect( - predictionsProvider.rekognitionClient.config.customUserAgent + (predictionsProvider as any).rekognitionClient.config.customUserAgent, ).toEqual( getAmplifyUserAgentObject({ category: Category.Predictions, action: PredictionsAction.Identify, - }) + }), ); }); test('identify for entities initializes a client with the correct custom user agent', async () => { @@ -759,12 +761,12 @@ describe('Predictions identify provider test', () => { await predictionsProvider.identify(detectFacesInput); expect( - predictionsProvider.rekognitionClient.config.customUserAgent + (predictionsProvider as any).rekognitionClient.config.customUserAgent, ).toEqual( getAmplifyUserAgentObject({ category: Category.Predictions, action: PredictionsAction.Identify, - }) + }), ); }); test('identify for text initializes a client with the correct custom user agent', async () => { @@ -777,19 +779,21 @@ describe('Predictions identify provider test', () => { await predictionsProvider.identify(detectTextInput); expect( - predictionsProvider.rekognitionClient.config.customUserAgent + (predictionsProvider as any).rekognitionClient.config.customUserAgent, ).toEqual( getAmplifyUserAgentObject({ category: Category.Predictions, action: PredictionsAction.Identify, - }) + }), ); - expect(predictionsProvider.textractClient.config.customUserAgent).toEqual( + expect( + (predictionsProvider as any).textractClient.config.customUserAgent, + ).toEqual( getAmplifyUserAgentObject({ category: Category.Predictions, action: PredictionsAction.Identify, - }) + }), ); }); }); diff --git a/packages/predictions/jest.config.js b/packages/predictions/jest.config.js deleted file mode 100644 index 8235622a9d1..00000000000 --- a/packages/predictions/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 64, - functions: 86, - lines: 89, - statements: 89, - }, - }, - moduleNameMapper: { - uuid: require.resolve('uuid'), - }, -}; diff --git a/packages/predictions/jest.config.mjs b/packages/predictions/jest.config.mjs new file mode 100644 index 00000000000..f62ee81ea8b --- /dev/null +++ b/packages/predictions/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 11, + functions: 24, + lines: 27, + statements: 27, + }, + }, + moduleNameMapper: { + uuid: requireResolve('uuid'), + }, +}); + +export default jestConfig; diff --git a/packages/predictions/package.json b/packages/predictions/package.json index f7c005d0b8a..82de8a7b4fe 100644 --- a/packages/predictions/package.json +++ b/packages/predictions/package.json @@ -11,21 +11,22 @@ "./dist/esm/Predictions.mjs" ], "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm run clean && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", "generate-docs-local": "typedoc --out docs src", "generate-docs-root": "typedoc --out ../../docs src", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 87.84" + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 87.84" }, "repository": { "type": "git", @@ -61,8 +62,7 @@ "devDependencies": { "@aws-amplify/core": "6.0.8", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/predictions/src/Predictions.ts b/packages/predictions/src/Predictions.ts index 97d699abb14..0b3721c094e 100644 --- a/packages/predictions/src/Predictions.ts +++ b/packages/predictions/src/Predictions.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ConsoleLogger } from '@aws-amplify/core'; + import { AmazonAIConvertPredictionsProvider, AmazonAIIdentifyPredictionsProvider, @@ -23,8 +23,6 @@ import { TranslateTextOutput, } from './types'; -const logger = new ConsoleLogger('Predictions'); - export class PredictionsClass { private convertProvider = new AmazonAIConvertPredictionsProvider(); private identifyProvider = new AmazonAIIdentifyPredictionsProvider(); @@ -42,7 +40,7 @@ export class PredictionsClass { public convert(input: TextToSpeechInput): Promise; public convert(input: SpeechToTextInput): Promise; public convert( - input: TranslateTextInput | TextToSpeechInput | SpeechToTextInput + input: TranslateTextInput | TextToSpeechInput | SpeechToTextInput, ): Promise { return this.convertProvider.convert(input); } @@ -50,10 +48,11 @@ export class PredictionsClass { public identify(input: IdentifyTextInput): Promise; public identify(input: IdentifyLabelsInput): Promise; public identify( - input: IdentifyEntitiesInput + input: IdentifyEntitiesInput, ): Promise; + public identify( - input: IdentifyTextInput | IdentifyLabelsInput | IdentifyEntitiesInput + input: IdentifyTextInput | IdentifyLabelsInput | IdentifyEntitiesInput, ): Promise< IdentifyTextOutput | IdentifyLabelsOutput | IdentifyEntitiesOutput > { diff --git a/packages/predictions/src/errors/utils/assertValidationError.ts b/packages/predictions/src/errors/utils/assertValidationError.ts index 16e70aa3740..042656d2d46 100644 --- a/packages/predictions/src/errors/utils/assertValidationError.ts +++ b/packages/predictions/src/errors/utils/assertValidationError.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { PredictionsError } from '../PredictionsError'; +import { PredictionsError } from '~/src/errors/PredictionsError'; import { PredictionsValidationErrorCode, validationErrorMap, -} from '../types/validation'; +} from '~/src/errors/types/validation'; export function assertValidationError( assertion: boolean, - name: PredictionsValidationErrorCode + name: PredictionsValidationErrorCode, ): asserts assertion { if (!assertion) { const { message, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/predictions/src/providers/AmazonAIConvertPredictionsProvider.ts b/packages/predictions/src/providers/AmazonAIConvertPredictionsProvider.ts index 77beadfd21d..01b6b73a0ec 100644 --- a/packages/predictions/src/providers/AmazonAIConvertPredictionsProvider.ts +++ b/packages/predictions/src/providers/AmazonAIConvertPredictionsProvider.ts @@ -1,10 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { Buffer } from 'buffer'; + import { Amplify, ConsoleLogger, fetchAuthSession } from '@aws-amplify/core'; import { AWSCredentials, Category, PredictionsAction, + // TODO(eslint): remove usage of deprecated API. Signer, getAmplifyUserAgentObject, } from '@aws-amplify/core/internals/utils'; @@ -18,9 +21,8 @@ import { MessageHeaderValue, } from '@smithy/eventstream-codec'; import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; -import { Buffer } from 'buffer'; -import { PredictionsValidationErrorCode } from '../errors/types/validation'; -import { assertValidationError } from '../errors/utils/assertValidationError'; +import { PredictionsValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; import { ConvertBytes, SpeechToTextInput, @@ -34,7 +36,7 @@ import { isTextToSpeechInput, isTranslateTextInput, isValidConvertInput, -} from '../types'; +} from '~/src/types'; const logger = new ConsoleLogger('AmazonAIConvertPredictionsProvider'); const eventBuilder = new EventStreamCodec(toUtf8, fromUtf8); @@ -50,27 +52,30 @@ export class AmazonAIConvertPredictionsProvider { } convert( - input: TranslateTextInput | TextToSpeechInput | SpeechToTextInput + input: TranslateTextInput | TextToSpeechInput | SpeechToTextInput, ): Promise { assertValidationError( isValidConvertInput(input), - PredictionsValidationErrorCode.InvalidInput + PredictionsValidationErrorCode.InvalidInput, ); if (isTranslateTextInput(input)) { logger.debug('translateText'); + return this.translateText(input); } else if (isTextToSpeechInput(input)) { logger.debug('textToSpeech'); + return this.convertTextToSpeech(input); } else { logger.debug('textToSpeech'); + return this.convertSpeechToText(input); } } protected async translateText( - input: TranslateTextInput + input: TranslateTextInput, ): Promise { logger.debug('Starting translation'); @@ -78,14 +83,14 @@ export class AmazonAIConvertPredictionsProvider { Amplify.getConfig().Predictions?.convert ?? {}; assertValidationError( !!translateText.region, - PredictionsValidationErrorCode.NoRegion + PredictionsValidationErrorCode.NoRegion, ); const { defaults = {}, region } = translateText; const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); const { sourceLanguage, targetLanguage } = defaults; @@ -95,11 +100,11 @@ export class AmazonAIConvertPredictionsProvider { input.translateText?.targetLanguage ?? targetLanguage; assertValidationError( !!sourceLanguageCode, - PredictionsValidationErrorCode.NoSourceLanguage + PredictionsValidationErrorCode.NoSourceLanguage, ); assertValidationError( !!targetLanguageCode, - PredictionsValidationErrorCode.NoTargetLanguage + PredictionsValidationErrorCode.NoTargetLanguage, ); this.translateClient = new TranslateClient({ @@ -116,6 +121,7 @@ export class AmazonAIConvertPredictionsProvider { Text: input.translateText?.source?.text, }); const data = await this.translateClient.send(translateTextCommand); + return { text: data.TranslatedText, language: data.TargetLanguageCode, @@ -123,22 +129,22 @@ export class AmazonAIConvertPredictionsProvider { } protected async convertTextToSpeech( - input: TextToSpeechInput + input: TextToSpeechInput, ): Promise { const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); assertValidationError( !!input.textToSpeech?.source, - PredictionsValidationErrorCode.NoSource + PredictionsValidationErrorCode.NoSource, ); const { speechGenerator } = Amplify.getConfig().Predictions?.convert ?? {}; assertValidationError( !!speechGenerator?.region, - PredictionsValidationErrorCode.NoRegion + PredictionsValidationErrorCode.NoRegion, ); const { defaults = {}, region } = speechGenerator; @@ -161,7 +167,6 @@ export class AmazonAIConvertPredictionsProvider { VoiceId: voiceId, TextType: 'text', SampleRate: '24000', - // tslint:disable-next-line: align }); const data = await this.pollyClient.send(synthesizeSpeechCommand); const response = new Response(data.AudioStream as ReadableStream); @@ -170,6 +175,7 @@ export class AmazonAIConvertPredictionsProvider { type: data.ContentType, }); const url = URL.createObjectURL(blob); + return { speech: { url }, audioStream: arrayBuffer, @@ -178,19 +184,19 @@ export class AmazonAIConvertPredictionsProvider { } protected async convertSpeechToText( - input: SpeechToTextInput + input: SpeechToTextInput, ): Promise { logger.debug('starting transcription..'); const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); const { transcription } = Amplify.getConfig().Predictions?.convert ?? {}; assertValidationError( !!transcription?.region, - PredictionsValidationErrorCode.NoRegion + PredictionsValidationErrorCode.NoRegion, ); const { defaults, region } = transcription; @@ -198,13 +204,13 @@ export class AmazonAIConvertPredictionsProvider { assertValidationError( !!language, - PredictionsValidationErrorCode.NoLanguage + PredictionsValidationErrorCode.NoLanguage, ); const source = input.transcription?.source; assertValidationError( isConvertBytesSource(source), - PredictionsValidationErrorCode.InvalidSource + PredictionsValidationErrorCode.InvalidSource, ); const connection = await this.openConnectionWithTranscribe({ @@ -218,6 +224,7 @@ export class AmazonAIConvertPredictionsProvider { raw: source.bytes, languageCode: language, }); + return { transcription: { fullText, @@ -232,7 +239,7 @@ export class AmazonAIConvertPredictionsProvider { if (transcribeMessage.headers[':message-type'].value === 'exception') { logger.debug( 'exception', - JSON.stringify(transcribeMessageJson.Message, null, 2) + JSON.stringify(transcribeMessageJson.Message, null, 2), ); throw new Error(transcribeMessageJson.Message); } else if (transcribeMessage.headers[':message-type'].value === 'event') { @@ -261,6 +268,7 @@ export class AmazonAIConvertPredictionsProvider { } } } + return decodedMessage; } @@ -269,31 +277,32 @@ export class AmazonAIConvertPredictionsProvider { raw, languageCode, }: TranscribeData): Promise { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { let fullText = ''; connection.onmessage = message => { try { const decodedMessage = AmazonAIConvertPredictionsProvider.serializeDataFromTranscribe( - message + message, ); if (decodedMessage) { fullText += decodedMessage + ' '; } } catch (err: unknown) { logger.debug(err); - rej(err); + reject(err); } }; connection.onerror = errorEvent => { logger.debug({ errorEvent }); - rej('failed to transcribe, network error'); + reject(new Error('failed to transcribe, network error')); }; connection.onclose = closeEvent => { logger.debug({ closeEvent }); - return res(fullText.trim()); + + resolve(fullText.trim()); }; logger.debug({ raw }); @@ -318,7 +327,7 @@ export class AmazonAIConvertPredictionsProvider { private sendEncodedDataToTranscribe( connection: WebSocket, data: ConvertBytes | any[], - languageCode: string + languageCode: string, ) { const downsampledBuffer = this.downsampleBuffer({ buffer: data, @@ -329,7 +338,7 @@ export class AmazonAIConvertPredictionsProvider { const pcmEncodedBuffer = this.pcmEncode(downsampledBuffer); const audioEventMessage = this.getAudioEventMessage( - Buffer.from(pcmEncodedBuffer) + Buffer.from(pcmEncodedBuffer), ); const binary = eventBuilder.encode(audioEventMessage); connection.send(binary); @@ -366,6 +375,7 @@ export class AmazonAIConvertPredictionsProvider { const s = Math.max(-1, Math.min(1, input[i])); view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true); } + return buffer; } @@ -393,8 +403,8 @@ export class AmazonAIConvertPredictionsProvider { let offsetBuffer = 0; while (offsetResult < result.length) { const nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio); - let accum = 0, - count = 0; + let accum = 0; + let count = 0; for ( let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; @@ -420,7 +430,9 @@ export class AmazonAIConvertPredictionsProvider { region: string; languageCode: string; }): Promise { - return new Promise(async (res, rej) => { + // TODO(eslint): remove this linter suppression by refactoring. + // eslint-disable-next-line no-async-promise-executor + return new Promise(async resolve => { const signedUrl = this.generateTranscribeUrl({ credentials, region, @@ -433,7 +445,7 @@ export class AmazonAIConvertPredictionsProvider { connection.binaryType = 'arraybuffer'; connection.onopen = () => { logger.debug('connected'); - res(connection); + resolve(connection); }; }); } @@ -462,12 +474,12 @@ export class AmazonAIConvertPredictionsProvider { }&`, `language-code=${languageCode}`, ].join(''); - + // TODO(eslint): remove usage of deprecated API. const signedUrl = Signer.signUrl( url, credentials, { region, service: 'transcribe' }, - 300 + 300, ); return signedUrl; diff --git a/packages/predictions/src/providers/AmazonAIIdentifyPredictionsProvider.ts b/packages/predictions/src/providers/AmazonAIIdentifyPredictionsProvider.ts index 5c749eb465d..54c5672008b 100644 --- a/packages/predictions/src/providers/AmazonAIIdentifyPredictionsProvider.ts +++ b/packages/predictions/src/providers/AmazonAIIdentifyPredictionsProvider.ts @@ -26,8 +26,8 @@ import { DetectDocumentTextCommandInput, TextractClient, } from '@aws-sdk/client-textract'; -import { PredictionsValidationErrorCode } from '../errors/types/validation'; -import { assertValidationError } from '../errors/utils/assertValidationError'; +import { PredictionsValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; import { BoundingBox, FaceAttributes, @@ -49,13 +49,9 @@ import { isIdentifyTextInput, isStorageSource, isValidIdentifyInput, -} from '../types'; -import { - BlockList, - Document, - Image, - TextDetectionList, -} from '../types/AWSTypes'; +} from '~/src/types'; +import { BlockList, Image, TextDetectionList } from '~/src/types/AWSTypes'; + import { categorizeRekognitionBlocks, categorizeTextractBlocks, @@ -73,23 +69,26 @@ export class AmazonAIIdentifyPredictionsProvider { } identify( - input: IdentifyTextInput | IdentifyLabelsInput | IdentifyEntitiesInput + input: IdentifyTextInput | IdentifyLabelsInput | IdentifyEntitiesInput, ): Promise< IdentifyTextOutput | IdentifyLabelsOutput | IdentifyEntitiesOutput > { assertValidationError( isValidIdentifyInput(input), - PredictionsValidationErrorCode.InvalidInput + PredictionsValidationErrorCode.InvalidInput, ); if (isIdentifyTextInput(input)) { logger.debug('identifyText'); + return this.identifyText(input); } else if (isIdentifyLabelsInput(input)) { logger.debug('identifyLabels'); + return this.identifyLabels(input); } else { logger.debug('identifyEntities'); + return this.identifyEntities(input); } } @@ -102,7 +101,7 @@ export class AmazonAIIdentifyPredictionsProvider { * @return {Promise} - Promise resolving to the converted source object. */ private configureSource(source: IdentifySource): Promise { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { if (isStorageSource(source)) { const storageConfig = { accessLevel: source.level, @@ -112,39 +111,46 @@ export class AmazonAIIdentifyPredictionsProvider { getUrl({ key: source.key, options: storageConfig }) .then(value => { const parser = - /https:\/\/([a-zA-Z0-9%\-_.]+)\.s3\.[A-Za-z0-9%\-._~]+\/([a-zA-Z0-9%\-._~/]+)\?/; + /https:\/\/([a-zA-Z0-9%\-_.]+)\.s3\.[A-Za-z0-9%\-._~]+\/([a-zA-Z0-9%\-._~/src/]+)\?/; const parsedURL = value.url.toString().match(parser) ?? ''; - if (parsedURL.length < 3) rej('Invalid S3 key was given.'); - res({ + if (parsedURL.length < 3) + reject(new Error('Invalid S3 key was given.')); + resolve({ S3Object: { Bucket: parsedURL[1], Name: decodeURIComponent(parsedURL[2]), }, }); }) - .catch(err => rej(err)); + .catch(err => { + reject(err); + }); } else if (isFileSource(source)) { blobToArrayBuffer(source.file) .then(buffer => { - res({ Bytes: new Uint8Array(buffer) }); + resolve({ Bytes: new Uint8Array(buffer) }); }) - .catch(err => rej(err)); + .catch(err => { + reject(err); + }); } else if (isIdentifyBytesSource(source)) { - const bytes = source.bytes; + const { bytes } = source; if (bytes instanceof Blob) { blobToArrayBuffer(bytes) .then(buffer => { - res({ Bytes: new Uint8Array(buffer) }); + resolve({ Bytes: new Uint8Array(buffer) }); }) - .catch(err => rej(err)); + .catch(err => { + reject(err); + }); } if (bytes instanceof ArrayBuffer || bytes instanceof Buffer) { - res({ Bytes: new Uint8Array(bytes) } as Image); + resolve({ Bytes: new Uint8Array(bytes) } as Image); } // everything else can be directly passed to Rekognition / Textract. - res({ Bytes: bytes } as Image); + resolve({ Bytes: bytes } as Image); } else { - rej('Input source is not configured correctly.'); + reject(new Error('Input source is not configured correctly.')); } }); } @@ -156,12 +162,12 @@ export class AmazonAIIdentifyPredictionsProvider { * @return {Promise} - Promise resolving to object containing identified texts. */ protected async identifyText( - input: IdentifyTextInput + input: IdentifyTextInput, ): Promise { const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); const { identifyText = {} } = @@ -179,9 +185,8 @@ export class AmazonAIIdentifyPredictionsProvider { credentials, customUserAgent: _getPredictionsIdentifyAmplifyUserAgent(), }); - let inputDocument: Document; - inputDocument = await this.configureSource(input.text?.source); + const inputDocument = await this.configureSource(input.text?.source); // get default value if format isn't specified in the input. const format = input.text?.format ?? configFormat; @@ -207,7 +212,7 @@ export class AmazonAIIdentifyPredictionsProvider { await this.rekognitionClient.send(detectTextCommand); const rekognitionResponse = categorizeRekognitionBlocks( - rekognitionData.TextDetections as TextDetectionList + rekognitionData.TextDetections as TextDetectionList, ); if (rekognitionResponse.text.words.length < 50) { // did not hit the word limit, return the data @@ -215,11 +220,11 @@ export class AmazonAIIdentifyPredictionsProvider { } const detectDocumentTextCommand = new DetectDocumentTextCommand( - textractParam + textractParam, ); const { Blocks } = await this.textractClient.send( - detectDocumentTextCommand + detectDocumentTextCommand, ); if ( @@ -237,6 +242,7 @@ export class AmazonAIIdentifyPredictionsProvider { const analyzeDocumentCommand = new AnalyzeDocumentCommand(param); const { Blocks } = await this.textractClient.send(analyzeDocumentCommand); + return categorizeTextractBlocks(Blocks as BlockList); } } @@ -247,12 +253,12 @@ export class AmazonAIIdentifyPredictionsProvider { * @return {Promise} - Promise resolving to an array of identified entities. */ protected async identifyLabels( - input: IdentifyLabelsInput + input: IdentifyLabelsInput, ): Promise { const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); const { identifyLabels = {} } = @@ -285,17 +291,18 @@ export class AmazonAIIdentifyPredictionsProvider { data.forEach(val => { identifyResult = { ...identifyResult, ...val }; }); + return identifyResult; }); } /** * Calls Rekognition.detectLabels and organizes the returned data. - * @param {DetectLabelsInput} param - parameter to be passed onto Rekognition + * @param {DetectLabelsCommandInput} param - parameter to be passed onto Rekognition * @return {Promise} - Promise resolving to organized detectLabels response. */ private async detectLabels( - param: DetectLabelsCommandInput + param: DetectLabelsCommandInput, ): Promise { const detectLabelsCommand = new DetectLabelsCommand(param); const data = await this.rekognitionClient!.send(detectLabelsCommand); @@ -304,8 +311,9 @@ export class AmazonAIIdentifyPredictionsProvider { const boxes = label.Instances?.map( instance => - makeCamelCase(instance.BoundingBox) as BoundingBox | undefined + makeCamelCase(instance.BoundingBox) as BoundingBox | undefined, ) || []; + return { name: label.Name, boundingBoxes: boxes, @@ -315,22 +323,23 @@ export class AmazonAIIdentifyPredictionsProvider { }, }; }); + return { labels: detectLabelData }; } /** * Calls Rekognition.detectModerationLabels and organizes the returned data. - * @param {Rekognition.DetectLabelsRequest} param - Parameter to be passed onto Rekognition + * @param {DetectModerationLabelsCommandInput} param - Parameter to be passed onto Rekognition * @return {Promise} - Promise resolving to organized detectModerationLabels response. */ private async detectModerationLabels( - param: DetectModerationLabelsCommandInput + param: DetectModerationLabelsCommandInput, ): Promise { const detectModerationLabelsCommand = new DetectModerationLabelsCommand( - param + param, ); const data = await this.rekognitionClient!.send( - detectModerationLabelsCommand + detectModerationLabelsCommand, ); if (data.ModerationLabels?.length !== 0) { return { unsafe: 'YES' }; @@ -342,16 +351,16 @@ export class AmazonAIIdentifyPredictionsProvider { /** * Identify faces within an image that is provided as input, and match faces from a collection * or identify celebrities. - * @param {IdentifyEntityInput} input - object containing the source image and face match options. - * @return {Promise} Promise resolving to identify results. + * @param {IdentifyEntitiesInput} input - object containing the source image and face match options. + * @return {Promise} Promise resolving to identify results. */ protected async identifyEntities( - input: IdentifyEntitiesInput + input: IdentifyEntitiesInput, ): Promise { const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); const { identifyEntities = {} } = @@ -381,13 +390,13 @@ export class AmazonAIIdentifyPredictionsProvider { ) { assertValidationError( celebrityDetectionEnabled, - PredictionsValidationErrorCode.CelebrityDetectionNotEnabled + PredictionsValidationErrorCode.CelebrityDetectionNotEnabled, ); const recognizeCelebritiesCommand = new RecognizeCelebritiesCommand( - param + param, ); const data = await this.rekognitionClient.send( - recognizeCelebritiesCommand + recognizeCelebritiesCommand, ); const faces = data.CelebrityFaces?.map( @@ -399,8 +408,9 @@ export class AmazonAIIdentifyPredictionsProvider { ...makeCamelCase(celebrity, ['Id', 'Name', 'Urls']), pose: makeCamelCase(celebrity.Face?.Pose), }, - }) as IdentifyEntity + }) as IdentifyEntity, ) ?? []; + return { entities: faces }; } else if ( isIdentifyFromCollection(input.entities) && @@ -417,7 +427,7 @@ export class AmazonAIIdentifyPredictionsProvider { MaxFaces: maxFaces, }; const searchFacesByImageCommand = new SearchFacesByImageCommand( - updatedParam + updatedParam, ); const data = await this.rekognitionClient.send(searchFacesByImageCommand); const faces = @@ -425,6 +435,7 @@ export class AmazonAIIdentifyPredictionsProvider { const externalImageId = match.Face?.ExternalImageId ? this.decodeExternalImageId(match.Face.ExternalImageId) : undefined; + return { boundingBox: makeCamelCase(match.Face?.BoundingBox), metadata: { @@ -433,6 +444,7 @@ export class AmazonAIIdentifyPredictionsProvider { }, } as IdentifyEntity; }) ?? []; + return { entities: faces }; } else { const detectFacesCommand = new DetectFacesCommand(param); @@ -452,12 +464,13 @@ export class AmazonAIIdentifyPredictionsProvider { ]; const faceAttributes = makeCamelCase( detail, - attributeKeys + attributeKeys, ) as FaceAttributes; faceAttributes.emotions = detail.Emotions?.map( - emotion => emotion.Type + emotion => emotion.Type, ); + return { boundingBox: makeCamelCase(detail.BoundingBox), landmarks: makeCamelCaseArray(detail.Landmarks), @@ -469,6 +482,7 @@ export class AmazonAIIdentifyPredictionsProvider { }, } as IdentifyEntity; }) ?? []; + return { entities: faces }; } } diff --git a/packages/predictions/src/providers/AmazonAIInterpretPredictionsProvider.ts b/packages/predictions/src/providers/AmazonAIInterpretPredictionsProvider.ts index 39093783449..20347f97ff8 100644 --- a/packages/predictions/src/providers/AmazonAIInterpretPredictionsProvider.ts +++ b/packages/predictions/src/providers/AmazonAIInterpretPredictionsProvider.ts @@ -6,7 +6,6 @@ import { PredictionsAction, getAmplifyUserAgentObject, } from '@aws-amplify/core/internals/utils'; - import { ComprehendClient, DetectDominantLanguageCommand, @@ -17,8 +16,8 @@ import { Entity, SyntaxToken, } from '@aws-sdk/client-comprehend'; -import { PredictionsValidationErrorCode } from '../errors/types/validation'; -import { assertValidationError } from '../errors/utils/assertValidationError'; +import { PredictionsValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; import { DetectParams, InterpretTextInput, @@ -30,7 +29,7 @@ import { TextSyntax, isInterpretTextOthers, isValidInterpretInput, -} from '../types'; +} from '~/src/types'; export class AmazonAIInterpretPredictionsProvider { private comprehendClient?: ComprehendClient; @@ -42,7 +41,7 @@ export class AmazonAIInterpretPredictionsProvider { interpret(input: InterpretTextInput): Promise { assertValidationError( isValidInterpretInput(input), - PredictionsValidationErrorCode.InvalidInput + PredictionsValidationErrorCode.InvalidInput, ); return this.interpretText(input); @@ -52,7 +51,7 @@ export class AmazonAIInterpretPredictionsProvider { const { credentials } = await fetchAuthSession(); assertValidationError( !!credentials, - PredictionsValidationErrorCode.NoCredentials + PredictionsValidationErrorCode.NoCredentials, ); const { interpretText = {} } = @@ -65,7 +64,10 @@ export class AmazonAIInterpretPredictionsProvider { const { text } = source; let language; if (isInterpretTextOthers(textSource)) { - language = (textSource as InterpretTextOthers).source.language; + const { + source: { language: sourceLanguage }, + } = textSource as InterpretTextOthers; + language = sourceLanguage; } this.comprehendClient = new ComprehendClient({ @@ -87,11 +89,11 @@ export class AmazonAIInterpretPredictionsProvider { languageCode = await this.detectLanguage(languageDetectionParams); } - let entitiesPromise: Promise> | undefined; + let entitiesPromise: Promise | undefined; if (doAll || type === 'entities') { assertValidationError( !!languageCode, - PredictionsValidationErrorCode.NoLanguage + PredictionsValidationErrorCode.NoLanguage, ); const entitiesDetectionParams = { Text: text, @@ -104,7 +106,7 @@ export class AmazonAIInterpretPredictionsProvider { if (doAll || type === 'sentiment') { assertValidationError( !!languageCode, - PredictionsValidationErrorCode.NoLanguage + PredictionsValidationErrorCode.NoLanguage, ); const sentimentParams = { Text: text, @@ -113,11 +115,11 @@ export class AmazonAIInterpretPredictionsProvider { sentimentPromise = this.detectSentiment(sentimentParams); } - let syntaxPromise: Promise> | undefined; + let syntaxPromise: Promise | undefined; if (doAll || type === 'syntax') { assertValidationError( !!languageCode, - PredictionsValidationErrorCode.NoLanguage + PredictionsValidationErrorCode.NoLanguage, ); const syntaxParams = { Text: text, @@ -130,7 +132,7 @@ export class AmazonAIInterpretPredictionsProvider { if (doAll || type === 'keyPhrases') { assertValidationError( !!languageCode, - PredictionsValidationErrorCode.NoLanguage + PredictionsValidationErrorCode.NoLanguage, ); const keyPhrasesParams = { @@ -145,6 +147,7 @@ export class AmazonAIInterpretPredictionsProvider { syntaxPromise, keyPhrasesPromise, ]); + return { textInterpretation: { keyPhrases, @@ -160,15 +163,16 @@ export class AmazonAIInterpretPredictionsProvider { try { const detectKeyPhrasesCommand = new DetectKeyPhrasesCommand(params); const data = await this.comprehendClient!.send(detectKeyPhrasesCommand); - const { KeyPhrases = [] } = data || {}; - return KeyPhrases.map(({ Text: text }) => { + const { KeyPhrases: keyPhrases = [] } = data || {}; + + return keyPhrases.map(({ Text: text }) => { return { text }; }); } catch (err: any) { if (err.code === 'AccessDeniedException') { throw new Error( 'Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' + - 'amplify predictions add' + 'amplify predictions add', ); } else { throw err; @@ -176,17 +180,18 @@ export class AmazonAIInterpretPredictionsProvider { } } - private async detectSyntax(params: DetectParams): Promise> { + private async detectSyntax(params: DetectParams): Promise { try { const detectSyntaxCommand = new DetectSyntaxCommand(params); const data = await this.comprehendClient!.send(detectSyntaxCommand); const { SyntaxTokens = [] } = data || {}; + return this.serializeSyntaxFromComprehend(SyntaxTokens); } catch (err: any) { if (err.code === 'AccessDeniedException') { throw new Error( 'Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' + - 'amplify predictions add' + 'amplify predictions add', ); } else { throw err; @@ -194,17 +199,16 @@ export class AmazonAIInterpretPredictionsProvider { } } - private serializeSyntaxFromComprehend( - tokens: SyntaxToken[] - ): Array { + private serializeSyntaxFromComprehend(tokens: SyntaxToken[]): TextSyntax[] { let response: TextSyntax[] = []; if (tokens && Array.isArray(tokens)) { response = tokens.map( ({ Text: text = '', PartOfSpeech: { Tag: syntax = '' } = {} }) => { return { text, syntax }; - } + }, ); } + return response; } @@ -220,13 +224,14 @@ export class AmazonAIInterpretPredictionsProvider { Neutral: neutral = 0, Mixed: mixed = 0, } = {}, - } = ({} = data); + } = data ?? {}; + return { predominant, positive, negative, neutral, mixed }; } catch (err: any) { if (err.code === 'AccessDeniedException') { throw new Error( 'Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' + - 'amplify predictions add' + 'amplify predictions add', ); } else { throw err; @@ -234,19 +239,18 @@ export class AmazonAIInterpretPredictionsProvider { } } - private async detectEntities( - params: DetectParams - ): Promise> { + private async detectEntities(params: DetectParams): Promise { try { const detectEntitiesCommand = new DetectEntitiesCommand(params); const data = await this.comprehendClient!.send(detectEntitiesCommand); const { Entities = [] } = data || {}; + return this.serializeEntitiesFromComprehend(Entities); } catch (err: any) { if (err.code === 'AccessDeniedException') { throw new Error( 'Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' + - 'amplify predictions add' + 'amplify predictions add', ); } else { throw err; @@ -254,28 +258,30 @@ export class AmazonAIInterpretPredictionsProvider { } } - private serializeEntitiesFromComprehend(data: Entity[]): Array { + private serializeEntitiesFromComprehend(data: Entity[]): TextEntities[] { let response: TextEntities[] = []; if (data && Array.isArray(data)) { response = data.map(({ Type: type, Text: text }) => { return { type, text }; }); } + return response; } private async detectLanguage(params: { Text: string }): Promise { try { const detectDominantLanguageCommand = new DetectDominantLanguageCommand( - params + params, ); const data = await this.comprehendClient!.send( - detectDominantLanguageCommand + detectDominantLanguageCommand, ); - const { Languages: [{ LanguageCode }] = [{}] } = ({} = data || {}); + const { Languages: [{ LanguageCode }] = [{ LanguageCode: undefined }] } = + data ?? {}; assertValidationError( !!LanguageCode, - PredictionsValidationErrorCode.NoLanguage + PredictionsValidationErrorCode.NoLanguage, ); return LanguageCode; @@ -283,7 +289,7 @@ export class AmazonAIInterpretPredictionsProvider { if (err.code === 'AccessDeniedException') { throw new Error( 'Not authorized, did you enable Interpret Text on predictions category Amplify CLI? try: ' + - 'amplify predictions add' + 'amplify predictions add', ); } else { throw err; diff --git a/packages/predictions/src/providers/IdentifyTextUtils.ts b/packages/predictions/src/providers/IdentifyTextUtils.ts index 047d987e802..87bd652e8cf 100644 --- a/packages/predictions/src/providers/IdentifyTextUtils.ts +++ b/packages/predictions/src/providers/IdentifyTextUtils.ts @@ -9,8 +9,9 @@ import { Polygon, Table, TableCell, -} from '../types'; -import { Block, BlockList, TextDetectionList } from '../types/AWSTypes'; +} from '~/src/types'; +import { Block, BlockList, TextDetectionList } from '~/src/types/AWSTypes'; + import { makeCamelCase, makeCamelCaseArray } from './Utils'; function getBoundingBox(geometry?: Geometry): BoundingBox | undefined { @@ -19,6 +20,7 @@ function getBoundingBox(geometry?: Geometry): BoundingBox | undefined { function getPolygon(geometry?: Geometry): Polygon | undefined { if (!geometry?.Polygon) return undefined; + return makeCamelCaseArray(Array.from(geometry.Polygon)) as Polygon; } @@ -29,7 +31,7 @@ function getPolygon(geometry?: Geometry): Polygon | undefined { * @return {IdentifyTextOutput} - Object that categorizes each block and its information. */ export function categorizeRekognitionBlocks( - blocks: TextDetectionList + blocks: TextDetectionList, ): IdentifyTextOutput { // Skeleton IdentifyText API response. We will populate it as we iterate through blocks. const response: IdentifyTextOutput = { @@ -67,8 +69,9 @@ export function categorizeRekognitionBlocks( // remove trailing space of fullText response.text.fullText = response.text.fullText.substr( 0, - response.text.fullText.length - 1 + response.text.fullText.length - 1, ); + return response; } @@ -79,7 +82,7 @@ export function categorizeRekognitionBlocks( * @return {IdentifyTextOutput} - Object that categorizes each block and its information. */ export function categorizeTextractBlocks( - blocks: BlockList + blocks: BlockList, ): IdentifyTextOutput { // Skeleton IdentifyText API response. We will populate it as we iterate through blocks. const response: IdentifyTextOutput = { @@ -102,9 +105,9 @@ export function categorizeTextractBlocks( * Note that we do not map `WORD` and `TABLE` in `blockMap` because they will not be referenced by any other * block except the Page block. */ - const tableBlocks: BlockList = Array(); - const keyValueBlocks: BlockList = Array(); - const blockMap: { [id: string]: Block } = {}; + const tableBlocks: BlockList = []; + const keyValueBlocks: BlockList = []; + const blockMap: Record = {}; blocks.forEach(block => { switch (block.BlockType) { @@ -130,9 +133,8 @@ export function categorizeTextractBlocks( blockMap[block.Id] = block; } break; - case 'SELECTION_ELEMENT': - const selectionStatus = - block.SelectionStatus === 'SELECTED' ? true : false; + case 'SELECTION_ELEMENT': { + const selectionStatus = block.SelectionStatus === 'SELECTED'; if (!response.text.selections) response.text.selections = []; response.text.selections.push({ selected: selectionStatus, @@ -143,6 +145,7 @@ export function categorizeTextractBlocks( blockMap[block.Id] = block; } break; + } case 'TABLE': tableBlocks.push(block); break; @@ -161,19 +164,19 @@ export function categorizeTextractBlocks( // remove trailing space in fullText response.text.fullText = response.text.fullText.substr( 0, - response.text.fullText.length - 1 + response.text.fullText.length - 1, ); // Post-process complex structures if they exist. if (tableBlocks.length !== 0) { - const tableResponse: Table[] = Array(); + const tableResponse: Table[] = []; tableBlocks.forEach(table => { tableResponse.push(constructTable(table, blockMap)); }); response.text.tables = tableResponse; } if (keyValueBlocks.length !== 0) { - const keyValueResponse: KeyValue[] = Array(); + const keyValueResponse: KeyValue[] = []; keyValueBlocks.forEach(keyValue => { // We need the KeyValue blocks of EntityType = `KEY`, which has both key and value references. if (keyValue.EntityTypes) { @@ -185,6 +188,7 @@ export function categorizeTextractBlocks( }); response.text.keyValues = keyValueResponse; } + return response; } @@ -193,12 +197,8 @@ export function categorizeTextractBlocks( * @param {Block} table - Table block that has references (`Relationships`) to its cells * @param {[id: string]: Block} blockMap - Maps block Ids to blocks. */ -function constructTable( - table: Block, - blockMap: { [key: string]: Block } -): Table { - let tableMatrix: TableCell[][]; - tableMatrix = []; +function constructTable(table: Block, blockMap: Record): Table { + const tableMatrix: TableCell[][] = []; // visit each of the cell associated with the table's relationship. for (const tableRelation of table.Relationships ?? []) { for (const cellId of tableRelation.Ids ?? []) { @@ -225,6 +225,7 @@ function constructTable( const columnSize = tableMatrix[0].length; const boundingBox = getBoundingBox(table.Geometry); const polygon = getPolygon(table.Geometry); + // Note that we leave spanned cells undefined for distinction return { size: { rows: rowSize, columns: columnSize }, @@ -241,11 +242,11 @@ function constructTable( */ function constructKeyValue( keyBlock: Block, - blockMap: { [key: string]: Block } + blockMap: Record, ): KeyValue { - let keyText: string = ''; - let valueText: string = ''; - let valueSelected: boolean = false; + let keyText = ''; + let valueText = ''; + let valueSelected = false; for (const keyValueRelation of keyBlock.Relationships ?? []) { if (keyValueRelation.Type === 'CHILD') { // relation refers to key @@ -261,6 +262,7 @@ function constructKeyValue( } } } + return { key: keyText, value: { text: valueText, selected: valueSelected }, @@ -276,10 +278,10 @@ function constructKeyValue( */ function extractContentsFromBlock( block: Block, - blockMap: { [id: string]: Block } + blockMap: Record, ): Content { - let words: string = ''; - let isSelected: boolean = false; + let words = ''; + let isSelected = false; if (!block.Relationships) { // some block might have no content @@ -291,11 +293,12 @@ function extractContentsFromBlock( if (contentBlock.BlockType === 'WORD') { words += contentBlock.Text + ' '; } else if (contentBlock.BlockType === 'SELECTION_ELEMENT') { - isSelected = contentBlock.SelectionStatus === 'SELECTED' ? true : false; + isSelected = contentBlock.SelectionStatus === 'SELECTED'; } } } words = words.substr(0, words.length - 1); // remove trailing space. + return { text: words, selected: isSelected }; } diff --git a/packages/predictions/src/providers/Utils.ts b/packages/predictions/src/providers/Utils.ts index efeb4313e44..d661ed170e4 100644 --- a/packages/predictions/src/providers/Utils.ts +++ b/packages/predictions/src/providers/Utils.ts @@ -7,14 +7,15 @@ export function makeCamelCase(obj?: any, keys?: string[]) { if (!obj) return undefined; const newObj = {}; - const keysToRename = keys ? keys : Object.keys(obj); + const keysToRename = keys || Object.keys(obj); keysToRename.forEach(key => { - if (obj.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { // change the key to camelcase. const camelCaseKey = key.charAt(0).toLowerCase() + key.substr(1); Object.assign(newObj, { [camelCaseKey]: obj[key] }); } }); + return newObj; } @@ -23,6 +24,7 @@ export function makeCamelCase(obj?: any, keys?: string[]) { */ export function makeCamelCaseArray(objArr?: object[], keys?: string[]) { if (!objArr) return undefined; + return objArr.map(obj => makeCamelCase(obj, keys)); } @@ -30,18 +32,18 @@ export function makeCamelCaseArray(objArr?: object[], keys?: string[]) { * Converts blob to array buffer */ export function blobToArrayBuffer(blob: Blob): Promise { - return new Promise((res, rej) => { + return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = _event => { - res(reader.result as Uint8Array); + resolve(reader.result as Uint8Array); }; reader.onerror = err => { - rej(err); + reject(err); }; try { reader.readAsArrayBuffer(blob); } catch (err) { - rej(err); // in case user gives invalid type + reject(err); // in case user gives invalid type } }); } diff --git a/packages/predictions/src/providers/index.ts b/packages/predictions/src/providers/index.ts index 7bec9e10f77..eedcb588280 100644 --- a/packages/predictions/src/providers/index.ts +++ b/packages/predictions/src/providers/index.ts @@ -3,6 +3,7 @@ import { AmazonAIConvertPredictionsProvider } from './AmazonAIConvertPredictionsProvider'; import { AmazonAIIdentifyPredictionsProvider } from './AmazonAIIdentifyPredictionsProvider'; import { AmazonAIInterpretPredictionsProvider } from './AmazonAIInterpretPredictionsProvider'; + export { AmazonAIConvertPredictionsProvider, AmazonAIIdentifyPredictionsProvider, diff --git a/packages/predictions/src/types/AWSTypes.ts b/packages/predictions/src/types/AWSTypes.ts index de0fb88509e..8da75e440c4 100644 --- a/packages/predictions/src/types/AWSTypes.ts +++ b/packages/predictions/src/types/AWSTypes.ts @@ -1,6 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/* tslint:disable:max-line-length */ import { Geometry, Relationship } from './Predictions'; export type BlockList = Block[]; @@ -61,13 +60,13 @@ export interface Block { /** *

A list of child blocks of the current block. For example a LINE object has child blocks for each WORD block that's part of the line of text. There aren't Relationship objects in the list for relationships that don't exist, such as when the current block has no child blocks. The list size can be the following:

  • 0 - The block has no child blocks.

  • 1 - The block has child blocks.

*/ - Relationships?: Array | Iterable; + Relationships?: Relationship[] | Iterable; /** *

The type of entity. The following can be returned:

  • KEY - An identifier for a field on the document.

  • VALUE - The field text.

EntityTypes isn't returned by DetectDocumentText and GetDocumentTextDetection.

*/ EntityTypes?: - | Array<'KEY' | 'VALUE' | string> + | ('KEY' | 'VALUE' | string)[] | Iterable<'KEY' | 'VALUE' | string>; /** diff --git a/packages/predictions/src/types/Predictions.ts b/packages/predictions/src/types/Predictions.ts index cc502c4e34b..04a2ad5de4e 100644 --- a/packages/predictions/src/types/Predictions.ts +++ b/packages/predictions/src/types/Predictions.ts @@ -1,6 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/* tslint:disable:max-line-length */ /** * Convert types @@ -49,9 +48,9 @@ export interface TextEntities { } export type KeyPhrases = KeyPhrase[]; -export type KeyPhrase = { +export interface KeyPhrase { text?: string; -}; +} export interface TextSyntax { text: string; @@ -66,18 +65,18 @@ export interface TextSentiment { mixed: number; } -export type DetectParams = { +export interface DetectParams { Text: string; LanguageCode: string; -}; +} export interface InterpretTextOutput { textInterpretation: { language?: string; - textEntities?: Array; + textEntities?: TextEntities[]; keyPhrases?: KeyPhrases; sentiment?: TextSentiment; - syntax?: Array; + syntax?: TextSyntax[]; }; } @@ -136,11 +135,11 @@ export interface SpeechToTextInput { }; } -export type TranscribeData = { +export interface TranscribeData { connection: WebSocket; raw: ConvertBytes; languageCode: string; -}; +} export interface SpeechToTextOutput { transcription: { @@ -181,8 +180,8 @@ export interface Content { export interface TableCell extends Content { boundingBox?: BoundingBox; polygon?: Polygon; - rowSpan?: Number; - columnSpan?: Number; + rowSpan?: number; + columnSpan?: number; } export interface Table { @@ -226,24 +225,24 @@ export interface IdentifyLabelsInput { } export interface Point { - x?: Number; - y?: Number; + x?: number; + y?: number; } -export type Polygon = Array | Iterable; +export type Polygon = Point[] | Iterable; export interface BoundingBox { - width?: Number; - height?: Number; - left?: Number; - top?: Number; + width?: number; + height?: number; + left?: number; + top?: number; } export interface IdentifyLabelsOutput { labels?: { name?: string; boundingBoxes?: (BoundingBox | undefined)[]; - metadata?: Object; + metadata?: object; }[]; unsafe?: 'YES' | 'NO' | 'UNKNOWN'; } @@ -280,18 +279,18 @@ export interface FaceAttributes { emotions?: (string | undefined)[]; } -export type EntityAgeRange = { - low?: Number; - high?: Number; -}; +export interface EntityAgeRange { + low?: number; + high?: number; +} -export type EntityLandmark = { +export interface EntityLandmark { type?: string; x?: number; y?: number; -}; +} -export type EntityMetadata = { +export interface EntityMetadata { id?: string; name?: string; pose?: { @@ -303,15 +302,15 @@ export type EntityMetadata = { externalImageId?: string; similarity?: number; confidence?: number; -}; +} -export type IdentifyEntity = { +export interface IdentifyEntity { boundingBox?: BoundingBox; ageRange?: EntityAgeRange; landmarks?: (EntityLandmark | undefined)[]; attributes?: FaceAttributes; metadata?: EntityMetadata; -}; +} export interface IdentifyEntitiesOutput { entities: IdentifyEntity[]; @@ -338,81 +337,98 @@ export function isValidInterpretInput(obj: any) { } export function isIdentifyFromCollection( - obj: any + obj: any, ): obj is IdentifyFromCollection { const key: keyof IdentifyFromCollection = 'collection'; const keyId: keyof IdentifyFromCollection = 'collectionId'; - return obj && (obj.hasOwnProperty(key) || obj.hasOwnProperty(keyId)); + + return ( + obj && + (Object.prototype.hasOwnProperty.call(obj, key) || + Object.prototype.hasOwnProperty.call(obj, keyId)) + ); } export function isIdentifyCelebrities(obj: any): obj is IdentifyCelebrities { const key: keyof IdentifyCelebrities = 'celebrityDetection'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isTranslateTextInput(obj: any): obj is TranslateTextInput { const key: keyof TranslateTextInput = 'translateText'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isTextToSpeechInput(obj: any): obj is TextToSpeechInput { const key: keyof TextToSpeechInput = 'textToSpeech'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isSpeechToTextInput(obj: any): obj is SpeechToTextInput { const key: keyof SpeechToTextInput = 'transcription'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isStorageSource(obj: any): obj is StorageSource { const key: keyof StorageSource = 'key'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isFileSource(obj: any): obj is FileSource { const key: keyof FileSource = 'file'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isConvertBytesSource( - obj: any + obj: any, ): obj is BytesSource { const key: keyof BytesSource = 'bytes'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isIdentifyBytesSource( - obj: any + obj: any, ): obj is BytesSource { const key: keyof BytesSource = 'bytes'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isIdentifyTextInput(obj: any): obj is IdentifyTextInput { const key: keyof IdentifyTextInput = 'text'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isIdentifyLabelsInput(obj: any): obj is IdentifyLabelsInput { const key: keyof IdentifyLabelsInput = 'labels'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isIdentifyEntitiesInput( - obj: any + obj: any, ): obj is IdentifyEntitiesInput { const key: keyof IdentifyEntitiesInput = 'entities'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isInterpretTextInput(obj: any): obj is InterpretTextInput { const key: keyof InterpretTextInput = 'text'; - return obj && obj.hasOwnProperty(key); + + return obj && Object.prototype.hasOwnProperty.call(obj, key); } export function isInterpretTextOthers( - text: InterpretTextInput['text'] + text: InterpretTextInput['text'], ): text is InterpretTextOthers { return (text as InterpretTextOthers).source.language !== undefined; } @@ -426,7 +442,7 @@ export interface Geometry { /** *

Within the bounding box, a fine-grained polygon around the detected text.

*/ - Polygon?: Array | Iterable; + Polygon?: Point[] | Iterable; } export interface Relationship { @@ -438,7 +454,7 @@ export interface Relationship { /** *

An array of IDs for related blocks. You can get the type of the relationship from the Type element.

*/ - Ids?: Array | Iterable; + Ids?: string[] | Iterable; } export type FeatureType = 'TABLES' | 'FORMS' | string; diff --git a/packages/predictions/tsconfig.build.json b/packages/predictions/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/predictions/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/predictions/tsconfig.json b/packages/predictions/tsconfig.json index 10adbc8c549..cc4ae13d539 100644 --- a/packages/predictions/tsconfig.json +++ b/packages/predictions/tsconfig.json @@ -1,8 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "strict": true, - "noImplicitAny": true + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["src"] + "include": ["./src", "__tests__"] } diff --git a/packages/predictions/tsconfig.test.json b/packages/predictions/tsconfig.test.json new file mode 100644 index 00000000000..51b154258d3 --- /dev/null +++ b/packages/predictions/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false, + "strict": false + } +} diff --git a/packages/predictions/tsconfig.watch.json b/packages/predictions/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/predictions/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/predictions/tslint.json b/packages/predictions/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/predictions/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/pubsub/.eslintrc.js b/packages/pubsub/.eslintrc.js new file mode 100644 index 00000000000..5dcb3b30a30 --- /dev/null +++ b/packages/pubsub/.eslintrc.js @@ -0,0 +1,17 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/pubsub', + }, + ], + }, + settings: { + 'import/ignore': ['*/paho-mqtt.js'], + }, +}; diff --git a/packages/pubsub/jest.config.js b/packages/pubsub/jest.config.js deleted file mode 100644 index b37fd6a8d1c..00000000000 --- a/packages/pubsub/jest.config.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 16, - functions: 41, - lines: 28, - statements: 28, - }, - }, - moduleNameMapper: { - uuid: require.resolve('uuid'), - }, -}; diff --git a/packages/pubsub/jest.config.mjs b/packages/pubsub/jest.config.mjs new file mode 100644 index 00000000000..60a2ceff43f --- /dev/null +++ b/packages/pubsub/jest.config.mjs @@ -0,0 +1,19 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import { requireResolve } from '../../jest/requireResolve.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 12, + functions: 41, + lines: 28, + statements: 28, + }, + }, + moduleNameMapper: { + uuid: requireResolve('uuid'), + }, +}); + +export default jestConfig; diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index 579c35e1e78..a417fddd830 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -11,19 +11,20 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:size": "size-limit", "build-with-test": "npm run clean && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs && cp -R src/vendor dist/cjs/vendor && cp -R src/vendor dist/esm/vendor", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf dist lib lib-esm", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 93.0 -i src/vendor/paho-mqtt.js" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 93.0 -i src/vendor/paho-mqtt.js" }, "typesVersions": { ">=4.2": { @@ -86,8 +87,7 @@ "devDependencies": { "@aws-amplify/core": "6.0.8", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "size-limit": [ { diff --git a/packages/pubsub/src/Providers/AWSIot.ts b/packages/pubsub/src/Providers/AWSIot.ts index 6e62283623e..87eac80206e 100644 --- a/packages/pubsub/src/Providers/AWSIot.ts +++ b/packages/pubsub/src/Providers/AWSIot.ts @@ -1,8 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { MqttOverWS, MqttOptions } from './MqttOverWS'; import { Signer } from '@aws-amplify/core/internals/utils'; import { fetchAuthSession } from '@aws-amplify/core'; + +import { MqttOptions, MqttOverWS } from './MqttOverWS'; + const SERVICE_NAME = 'iotdevicegateway'; export interface AWSIoTOptions extends MqttOptions { @@ -21,7 +23,7 @@ export class AWSIoT extends MqttOverWS { protected get endpoint() { return (async () => { - const endpoint = this.options.endpoint; + const {endpoint} = this.options; const serviceInfo = { service: SERVICE_NAME, @@ -42,7 +44,7 @@ export class AWSIoT extends MqttOverWS { const result = Signer.signUrl( endpoint, { access_key, secret_key, session_token }, - serviceInfo + serviceInfo, ); return result; diff --git a/packages/pubsub/src/Providers/MqttOverWS.ts b/packages/pubsub/src/Providers/MqttOverWS.ts index ea943b88bbb..ddf7a58df22 100644 --- a/packages/pubsub/src/Providers/MqttOverWS.ts +++ b/packages/pubsub/src/Providers/MqttOverWS.ts @@ -1,36 +1,38 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +// TODO(eslint): remove this linter suppression. +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import * as Paho from '../vendor/paho-mqtt.js'; -import { Observable, SubscriptionLike as Subscription, Observer } from 'rxjs'; - -import { AbstractPubSub } from './PubSub'; +import * as Paho from '~/src/vendor/paho-mqtt.js'; +import { Observable, Observer, SubscriptionLike as Subscription } from 'rxjs'; import { ConnectionState, - PubSubContentObserver, PubSubContent, + PubSubContentObserver, PubSubOptions, PublishInput, SubscribeInput, -} from '../types/PubSub'; -import { Hub, HubPayload, ConsoleLogger } from '@aws-amplify/core'; +} from '~/src/types/PubSub'; +import { ConsoleLogger, Hub, HubPayload } from '@aws-amplify/core'; import { amplifyUuid } from '@aws-amplify/core/internals/utils'; import { - ConnectionStateMonitor, CONNECTION_CHANGE, -} from '../utils/ConnectionStateMonitor'; + ConnectionStateMonitor, +} from '~/src/utils/ConnectionStateMonitor'; import { ReconnectEvent, ReconnectionMonitor, -} from '../utils/ReconnectionMonitor'; +} from '~/src/utils/ReconnectionMonitor'; + +import { AbstractPubSub } from './PubSub'; import { AMPLIFY_SYMBOL, CONNECTION_STATE_CHANGE } from './constants'; const logger = new ConsoleLogger('MqttOverWS'); export function mqttTopicMatch(filter: string, topic: string) { const filterArray = filter.split('/'); - const length = filterArray.length; + const { length } = filterArray; const topicArray = topic.split('/'); for (let i = 0; i < length; ++i) { @@ -39,6 +41,7 @@ export function mqttTopicMatch(filter: string, topic: string) { if (left === '#') return topicArray.length >= length; if (left !== '+' && left !== right) return false; } + return length === topicArray.length; } @@ -49,27 +52,27 @@ export interface MqttOptions extends PubSubOptions { } interface PahoClient { - onMessageArrived: (params: { + onMessageArrived(params: { destinationName: string; payloadString: string; - }) => void; - onConnectionLost: (params: { errorCode: number }) => void; - connect: (params: { - [k: string]: string | number | boolean | (() => void); - }) => void; - disconnect: () => void; - isConnected: () => boolean; - subscribe: (topic: string) => void; - unsubscribe: (topic: string) => void; + }): void; + onConnectionLost(params: { errorCode: number }): void; + connect( + params: Record void)>, + ): void; + disconnect(): void; + isConnected(): boolean; + subscribe(topic: string): void; + unsubscribe(topic: string): void; send(topic: string, message: string): void; } class ClientsQueue { - private promises: Map> = new Map(); + private promises = new Map>(); async get( clientId: string, - clientFactory?: (input: string) => Promise + clientFactory?: (input: string) => Promise, ) { const cachedPromise = this.promises.get(clientId); if (cachedPromise) return cachedPromise; @@ -78,6 +81,7 @@ class ClientsQueue { const newPromise = clientFactory(clientId); this.promises.set(clientId, newPromise); newPromise.catch(() => this.promises.delete(clientId)); + return newPromise; } @@ -129,7 +133,7 @@ export class MqttOverWS extends AbstractPubSub { // Trigger connected to halt reconnection attempts this.reconnectionMonitor.record(ReconnectEvent.HALT_RECONNECT); } - } + }, ); } @@ -146,9 +150,8 @@ export class MqttOverWS extends AbstractPubSub { } protected get isSSLEnabled() { - return !this.options[ - 'aws_appsync_dangerously_connect_to_http_endpoint_for_testing' - ]; + return !this.options + .aws_appsync_dangerously_connect_to_http_endpoint_for_testing; } public onDisconnect({ @@ -177,7 +180,7 @@ export class MqttOverWS extends AbstractPubSub { logger.debug('Creating new MQTT client', clientId); this.connectionStateMonitor.record(CONNECTION_CHANGE.OPENING_CONNECTION); - // @ts-ignore + const client = new Paho.Client(url, clientId) as PahoClient; client.onMessageArrived = ({ @@ -199,11 +202,13 @@ export class MqttOverWS extends AbstractPubSub { this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED); }; - const connected = await new Promise((resolve, reject) => { + const connected = await new Promise(resolve => { client.connect({ useSSL: this.isSSLEnabled, mqttVersion: 3, - onSuccess: () => resolve(true), + onSuccess: () => { + resolve(true); + }, onFailure: () => { if (clientId) this._clientsQueue.remove(clientId); this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED); @@ -214,7 +219,7 @@ export class MqttOverWS extends AbstractPubSub { if (connected) { this.connectionStateMonitor.record( - CONNECTION_CHANGE.CONNECTION_ESTABLISHED + CONNECTION_CHANGE.CONNECTION_ESTABLISHED, ); } @@ -223,19 +228,23 @@ export class MqttOverWS extends AbstractPubSub { protected async connect( clientId: string, - options: MqttOptions = {} + options: MqttOptions = {}, ): Promise { - return await this.clientsQueue.get(clientId, async clientId => { - const client = await this.newClient({ ...options, clientId }); + return this.clientsQueue.get(clientId, async clientIdForNewClient => { + const client = await this.newClient({ + ...options, + clientId: clientIdForNewClient, + }); if (client) { // Once connected, subscribe to all topics registered observers this._topicObservers.forEach( (_value: Set, key: string) => { client.subscribe(key); - } + }, ); } + return client; }); } @@ -258,21 +267,21 @@ export class MqttOverWS extends AbstractPubSub { if (client) { logger.debug('Publishing to topic(s)', targetTopics.join(','), message); - targetTopics.forEach(topic => client.send(topic, msg)); + targetTopics.forEach(topic => { + client.send(topic, msg); + }); } else { logger.debug( 'Publishing to topic(s) failed', targetTopics.join(','), - message + message, ); } } - protected _topicObservers: Map> = - new Map(); + protected _topicObservers = new Map>(); - protected _clientIdObservers: Map> = - new Map(); + protected _clientIdObservers = new Map>(); private _onMessage(topic: string, msg: string) { try { @@ -285,12 +294,13 @@ export class MqttOverWS extends AbstractPubSub { const parsedMessage: PubSubContent = JSON.parse(msg); if (typeof parsedMessage === 'object') { - // @ts-ignore parsedMessage[topicSymbol] = topic; } matchedTopicObservers.forEach(observersForTopic => { - observersForTopic.forEach(observer => observer.next(parsedMessage)); + observersForTopic.forEach(observer => { + observer.next(parsedMessage); + }); }); } catch (error) { logger.warn('Error handling message', error, msg); @@ -350,8 +360,8 @@ export class MqttOverWS extends AbstractPubSub { await getClient(); // Add an observable to the reconnection list to manage reconnection for this subscription - reconnectSubscription = new Observable(observer => { - this.reconnectionMonitor.addObserver(observer); + reconnectSubscription = new Observable(reconnectionObserver => { + this.reconnectionMonitor.addObserver(reconnectionObserver); }).subscribe(() => { getClient(); }); @@ -368,7 +378,7 @@ export class MqttOverWS extends AbstractPubSub { if (this._clientIdObservers.get(clientId)?.size === 0) { this.disconnect(clientId); this.connectionStateMonitor.record( - CONNECTION_CHANGE.CLOSING_CONNECTION + CONNECTION_CHANGE.CLOSING_CONNECTION, ); this._clientIdObservers.delete(clientId); } diff --git a/packages/pubsub/src/Providers/PubSub.ts b/packages/pubsub/src/Providers/PubSub.ts index 5750d0a1940..8faf96a6872 100644 --- a/packages/pubsub/src/Providers/PubSub.ts +++ b/packages/pubsub/src/Providers/PubSub.ts @@ -3,12 +3,13 @@ import { Observable } from 'rxjs'; import { PubSubBase, - PubSubOptions, PubSubContent, + PubSubOptions, PublishInput, SubscribeInput, -} from '../types/PubSub'; +} from '~/src/types/PubSub'; import { ConsoleLogger } from '@aws-amplify/core'; + const logger = new ConsoleLogger('AbstractPubSubProvider'); export abstract class AbstractPubSub diff --git a/packages/pubsub/src/types/PubSub.ts b/packages/pubsub/src/types/PubSub.ts index 40b3f6eca25..87a9909e32a 100644 --- a/packages/pubsub/src/types/PubSub.ts +++ b/packages/pubsub/src/types/PubSub.ts @@ -1,6 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Observer, Observable } from 'rxjs'; +import { Observable, Observer } from 'rxjs'; + export interface SubscriptionObserver { closed: boolean; next(value: T): void; @@ -52,7 +53,7 @@ export enum ConnectionState { ConnectedPendingKeepAlive = 'ConnectedPendingKeepAlive', } -export type PubSubContent = Record; +export type PubSubContent = Record; export type PubSubContentObserver = Observer; export interface PubSubOptions { @@ -69,13 +70,13 @@ export interface PubSubBase { subscribe(input: SubscribeInput): Observable; } -export type PublishInput = { +export interface PublishInput { topics: string[] | string; message: PubSubContent; options?: PubSubOptions; -}; +} -export type SubscribeInput = { +export interface SubscribeInput { topics: string[] | string; options?: PubSubOptions; -}; +} diff --git a/packages/pubsub/src/utils/ConnectionStateMonitor.ts b/packages/pubsub/src/utils/ConnectionStateMonitor.ts index daada3eed40..0ac5897a40d 100644 --- a/packages/pubsub/src/utils/ConnectionStateMonitor.ts +++ b/packages/pubsub/src/utils/ConnectionStateMonitor.ts @@ -1,19 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { Observable, Observer, SubscriptionLike, map, filter } from 'rxjs'; -import { ConnectionState } from '../types/PubSub'; +import { Observable, Observer, SubscriptionLike, filter, map } from 'rxjs'; +import { ConnectionState } from '~/src/types/PubSub'; + import { ReachabilityMonitor } from './ReachabilityMonitor'; // Internal types for tracking different connection states type LinkedConnectionState = 'connected' | 'disconnected'; type LinkedHealthState = 'healthy' | 'unhealthy'; -type LinkedConnectionStates = { +interface LinkedConnectionStates { networkState: LinkedConnectionState; connectionState: LinkedConnectionState | 'connecting'; intendedConnectionState: LinkedConnectionState; keepAliveState: LinkedHealthState; -}; +} export const CONNECTION_CHANGE: { [key in @@ -67,10 +68,10 @@ export class ConnectionStateMonitor { this._initialNetworkStateSubscription = ReachabilityMonitor().subscribe( ({ online }) => { this.record( - online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE + online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE, ); this._initialNetworkStateSubscription?.unsubscribe(); - } + }, ); this._linkedConnectionStateObservable = @@ -92,9 +93,9 @@ export class ConnectionStateMonitor { this._networkMonitoringSubscription = ReachabilityMonitor().subscribe( ({ online }) => { this.record( - online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE + online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE, ); - } + }, ); } } @@ -124,14 +125,15 @@ export class ConnectionStateMonitor { .pipe( map(value => { return this.connectionStatesTranslator(value); - }) + }), ) .pipe( filter(current => { const toInclude = current !== previous; previous = current; + return toInclude; - }) + }), ); } @@ -194,6 +196,7 @@ export class ConnectionStateMonitor { // All remaining states directly correspond to the connection state if (connectionState === 'connecting') return ConnectionState.Connecting; if (connectionState === 'disconnected') return ConnectionState.Disconnected; + return ConnectionState.Connected; } } diff --git a/packages/pubsub/src/utils/ReachabilityMonitor/index.native.ts b/packages/pubsub/src/utils/ReachabilityMonitor/index.native.ts index 990a90be3a2..84ce6461017 100644 --- a/packages/pubsub/src/utils/ReachabilityMonitor/index.native.ts +++ b/packages/pubsub/src/utils/ReachabilityMonitor/index.native.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Reachability } from '@aws-amplify/core/internals/utils'; -import { default as NetInfo } from '@react-native-community/netinfo'; +import NetInfo from '@react-native-community/netinfo'; export const ReachabilityMonitor = () => new Reachability().networkMonitor(NetInfo); diff --git a/packages/pubsub/src/utils/ReconnectionMonitor.ts b/packages/pubsub/src/utils/ReconnectionMonitor.ts index 50ada9cf1b5..c750afbbdf8 100644 --- a/packages/pubsub/src/utils/ReconnectionMonitor.ts +++ b/packages/pubsub/src/utils/ReconnectionMonitor.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { Observer } from 'rxjs'; -import { RECONNECT_DELAY, RECONNECT_INTERVAL } from '../Providers/constants'; +import { RECONNECT_DELAY, RECONNECT_INTERVAL } from '~/src/Providers/constants'; export enum ReconnectEvent { START_RECONNECT = 'START_RECONNECT', diff --git a/packages/pubsub/tsconfig.build.json b/packages/pubsub/tsconfig.build.json new file mode 100644 index 00000000000..b202c57cc23 --- /dev/null +++ b/packages/pubsub/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__", "__mocks__"] +} diff --git a/packages/pubsub/tsconfig.json b/packages/pubsub/tsconfig.json index cb4becbc018..cb483eac500 100644 --- a/packages/pubsub/tsconfig.json +++ b/packages/pubsub/tsconfig.json @@ -1,10 +1,11 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true, - "outDir": "./lib/" + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src/**/*.ts"] + "include": ["./src", "__tests__", "__mocks__"] } diff --git a/packages/pubsub/tsconfig.test.json b/packages/pubsub/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/pubsub/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/pubsub/tsconfig.watch.json b/packages/pubsub/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/pubsub/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/pubsub/tslint.json b/packages/pubsub/tslint.json deleted file mode 100644 index 1bb9e144d24..00000000000 --- a/packages/pubsub/tslint.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/react-native/.eslintrc.js b/packages/react-native/.eslintrc.js new file mode 100644 index 00000000000..1bf0752cb72 --- /dev/null +++ b/packages/react-native/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/react-native', + }, + ], + }, +}; diff --git a/packages/react-native/example/src/App.tsx b/packages/react-native/example/src/App.tsx index 3e4ca40fe59..11aec8bc3b2 100644 --- a/packages/react-native/example/src/App.tsx +++ b/packages/react-native/example/src/App.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import * as React from 'react'; - -import { StyleSheet, View, Text, Platform } from 'react-native'; +import { Platform, StyleSheet, Text, View } from 'react-native'; import { computeModPow, computeS } from '@aws-amplify/react-native'; const computeModPowPayload = { @@ -31,10 +30,10 @@ export default function App() { React.useEffect(() => { async function calc() { - const computeModPowResult = await computeModPow(computeModPowPayload); - setComputeModPowResult(computeModPowResult); - const computeSResult = await computeS(computeSPayload); - setComputeSResult(computeSResult); + const newComputeModPowResult = await computeModPow(computeModPowPayload); + setComputeModPowResult(newComputeModPowResult); + const newComputeSResult = await computeS(computeSPayload); + setComputeSResult(newComputeSResult); } calc(); }, []); diff --git a/packages/react-native/jest.config.js b/packages/react-native/jest.config.js deleted file mode 100644 index 3a0e3340f9f..00000000000 --- a/packages/react-native/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100, - }, - }, -}; diff --git a/packages/react-native/jest.config.mjs b/packages/react-native/jest.config.mjs new file mode 100644 index 00000000000..7c9c9dd41cb --- /dev/null +++ b/packages/react-native/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); + +export default jestConfig; diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 4668bd70e05..cbc43a5cc57 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -11,17 +11,18 @@ "access": "public" }, "scripts": { - "test": "tslint 'src/**/*.ts'", + "test": "npm run lint", "test:android": "./android/gradlew test -p ./android", "build-with-test": "npm run clean && npm test && tsc", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -p ./tsconfig.build.json -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -p ./tsconfig.build.json -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs", "clean": "rimraf lib-esm lib dist", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.build.json -t 88.21" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 88.21" }, "dependencies": { "base-64": "^1.0.0", @@ -42,8 +43,7 @@ "@types/react-native": "0.70.0", "react-native": "0.70.0", "react-native-get-random-values": "1.9.0", - "rollup": "3.29.4", - "typescript": "5.1.6" + "rollup": "3.29.4" }, "repository": { "type": "git", diff --git a/packages/react-native/src/apis/computeModPow.ts b/packages/react-native/src/apis/computeModPow.ts index 1bb6d7032cb..a2ff453c52d 100644 --- a/packages/react-native/src/apis/computeModPow.ts +++ b/packages/react-native/src/apis/computeModPow.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; -import { RTNCore } from '../types'; +import { nativeModule } from '~/src/nativeModule'; +import { RTNCore } from '~/src/types'; export const computeModPow: RTNCore['computeModPow'] = payload => nativeModule.computeModPow(payload); diff --git a/packages/react-native/src/apis/computeS.ts b/packages/react-native/src/apis/computeS.ts index 47e244c17b1..a2701e7004a 100644 --- a/packages/react-native/src/apis/computeS.ts +++ b/packages/react-native/src/apis/computeS.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; -import { RTNCore } from '../types'; +import { nativeModule } from '~/src/nativeModule'; +import { RTNCore } from '~/src/types'; export const computeS: RTNCore['computeS'] = payload => nativeModule.computeS(payload); diff --git a/packages/react-native/src/moduleLoaders/loadAmplifyPushNotification.ts b/packages/react-native/src/moduleLoaders/loadAmplifyPushNotification.ts index 0b23cc1900e..a86697e4e8c 100644 --- a/packages/react-native/src/moduleLoaders/loadAmplifyPushNotification.ts +++ b/packages/react-native/src/moduleLoaders/loadAmplifyPushNotification.ts @@ -13,7 +13,7 @@ export const loadAmplifyPushNotification = () => { } throw new Error( - 'Ensure `@aws-amplify/rtn-push-notification` is installed and linked.' + 'Ensure `@aws-amplify/rtn-push-notification` is installed and linked.', ); } catch (e) { // The error parsing logic cannot be extracted with metro as the `require` @@ -21,7 +21,7 @@ export const loadAmplifyPushNotification = () => { // another module and that causes an error const message = (e as Error).message.replace( /undefined/g, - '@aws-amplify/rtn-push-notification' + '@aws-amplify/rtn-push-notification', ); throw new Error(message); } diff --git a/packages/react-native/src/moduleLoaders/loadAmplifyWebBrowser.ts b/packages/react-native/src/moduleLoaders/loadAmplifyWebBrowser.ts index 29611beae02..2c98a39f8f6 100644 --- a/packages/react-native/src/moduleLoaders/loadAmplifyWebBrowser.ts +++ b/packages/react-native/src/moduleLoaders/loadAmplifyWebBrowser.ts @@ -13,7 +13,7 @@ export const loadAmplifyWebBrowser = () => { } throw new Error( - 'Ensure `@aws-amplify/rtn-web-browser` is installed and linked.' + 'Ensure `@aws-amplify/rtn-web-browser` is installed and linked.', ); } catch (e) { // The error parsing logic cannot be extracted with metro as the `require` @@ -21,7 +21,7 @@ export const loadAmplifyWebBrowser = () => { // another module and that causes an error const message = (e as Error).message.replace( /undefined/g, - '@aws-amplify/rtn-web-browser' + '@aws-amplify/rtn-web-browser', ); throw new Error(message); } diff --git a/packages/react-native/src/moduleLoaders/loadAsyncStorage.ts b/packages/react-native/src/moduleLoaders/loadAsyncStorage.ts index 2c3cf052a2f..ae5de3ec2bd 100644 --- a/packages/react-native/src/moduleLoaders/loadAsyncStorage.ts +++ b/packages/react-native/src/moduleLoaders/loadAsyncStorage.ts @@ -14,7 +14,7 @@ export const loadAsyncStorage = (): AsyncStorageStatic => { } throw new Error( - 'Ensure `@react-native-async-storage/async-storage` is installed and linked.' + 'Ensure `@react-native-async-storage/async-storage` is installed and linked.', ); } catch (e) { // The error parsing logic cannot be extracted with metro as the `require` @@ -22,7 +22,7 @@ export const loadAsyncStorage = (): AsyncStorageStatic => { // another module and that causes an error const message = (e as Error).message.replace( /undefined/g, - '@react-native-async-storage/async-storage' + '@react-native-async-storage/async-storage', ); throw new Error(message); } diff --git a/packages/react-native/src/moduleLoaders/loadGetRandomValues.ts b/packages/react-native/src/moduleLoaders/loadGetRandomValues.ts index ca315df3340..53d3320b99b 100644 --- a/packages/react-native/src/moduleLoaders/loadGetRandomValues.ts +++ b/packages/react-native/src/moduleLoaders/loadGetRandomValues.ts @@ -12,7 +12,7 @@ export const loadGetRandomValues = () => { // another module and that causes error const message = (e as Error).message.replace( /undefined/g, - 'react-native-get-random-values' + 'react-native-get-random-values', ); throw new Error(message); } diff --git a/packages/react-native/src/moduleLoaders/loadNetInfo.ts b/packages/react-native/src/moduleLoaders/loadNetInfo.ts index 9601026b602..def2c04c602 100644 --- a/packages/react-native/src/moduleLoaders/loadNetInfo.ts +++ b/packages/react-native/src/moduleLoaders/loadNetInfo.ts @@ -16,7 +16,7 @@ export const loadNetInfo = (): NetInfoModule => { } throw new Error( - 'Ensure `@react-native-community/netinfo` is installed and linked.' + 'Ensure `@react-native-community/netinfo` is installed and linked.', ); } catch (e) { // The error parsing logic cannot be extract as with metro the `require` @@ -24,7 +24,7 @@ export const loadNetInfo = (): NetInfoModule => { // another module and that causes error const message = (e as Error).message.replace( /undefined/g, - '@react-native-community/netinfo' + '@react-native-community/netinfo', ); throw new Error(message); } diff --git a/packages/react-native/src/moduleLoaders/loadUrlPolyfill.ts b/packages/react-native/src/moduleLoaders/loadUrlPolyfill.ts index 3c64c33a220..39393732d06 100644 --- a/packages/react-native/src/moduleLoaders/loadUrlPolyfill.ts +++ b/packages/react-native/src/moduleLoaders/loadUrlPolyfill.ts @@ -12,7 +12,7 @@ export const loadUrlPolyfill = () => { // another module and that causes error const message = (e as Error).message.replace( /undefined/g, - 'react-native-url-polyfill' + 'react-native-url-polyfill', ); throw new Error(message); } diff --git a/packages/react-native/src/nativeModule.ts b/packages/react-native/src/nativeModule.ts index 062c73d9c3a..97d598c856d 100644 --- a/packages/react-native/src/nativeModule.ts +++ b/packages/react-native/src/nativeModule.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { NativeModules, Platform } from 'react-native'; + import { RTNCore } from './types'; const LINKING_ERROR = @@ -18,5 +19,5 @@ export const nativeModule: RTNCore = NativeModules.AmplifyRTNCore get() { throw new Error(LINKING_ERROR); }, - } + }, ); diff --git a/packages/react-native/tsconfig.build.json b/packages/react-native/tsconfig.build.json index 507d6e739b0..91c33fee6d5 100644 --- a/packages/react-native/tsconfig.build.json +++ b/packages/react-native/tsconfig.build.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", "include": ["src"], - "exclude": ["example"] + "exclude": ["example", "__tests__"] } diff --git a/packages/react-native/tsconfig.json b/packages/react-native/tsconfig.json index eee06a92ae0..1630ff61109 100755 --- a/packages/react-native/tsconfig.json +++ b/packages/react-native/tsconfig.json @@ -1,16 +1,12 @@ -//WARNING: If you are manually specifying files to compile then the tsconfig.json is completely ignored, you must use command line flags { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { + "rootDirs": ["src"], + "baseUrl": ".", "paths": { - "@aws-amplify/react-native": ["./src/index"] + "@aws-amplify/react-native": ["./src/index"], + "~/*": ["./*"] }, - "allowSyntheticDefaultImports": true, - "noImplicitAny": true, - "jsx": "react", - "lib": ["esnext"], - "resolveJsonModule": true, - "strict": true, - "skipLibCheck": true + "jsx": "react" } } diff --git a/packages/react-native/tsconfig.test.json b/packages/react-native/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/react-native/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/react-native/tsconfig.watch.json b/packages/react-native/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/react-native/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/react-native/tslint.json b/packages/react-native/tslint.json deleted file mode 100644 index 8eafab1d2b4..00000000000 --- a/packages/react-native/tslint.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/rtn-push-notification/.eslintrc.js b/packages/rtn-push-notification/.eslintrc.js new file mode 100644 index 00000000000..889e9330c7e --- /dev/null +++ b/packages/rtn-push-notification/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/rtn-push-notification', + }, + ], + }, +}; diff --git a/packages/rtn-push-notification/jest.config.js b/packages/rtn-push-notification/jest.config.js deleted file mode 100644 index cbee5767b39..00000000000 --- a/packages/rtn-push-notification/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 94, - functions: 100, - lines: 100, - statements: 100, - }, - }, -}; diff --git a/packages/rtn-push-notification/jest.config.mjs b/packages/rtn-push-notification/jest.config.mjs new file mode 100644 index 00000000000..bb30851349c --- /dev/null +++ b/packages/rtn-push-notification/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 94, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); + +export default jestConfig; diff --git a/packages/rtn-push-notification/package.json b/packages/rtn-push-notification/package.json index 1be011a07bf..41dacb21fea 100644 --- a/packages/rtn-push-notification/package.json +++ b/packages/rtn-push-notification/package.json @@ -11,23 +11,23 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "test:android": "./android/gradlew test -p ./android", "build-with-test": "npm run clean && npm test && tsc", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs", "clean": "rimraf lib-esm lib dist", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.json -t 99" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 99" }, "devDependencies": { "@rollup/plugin-typescript": "11.1.5", "@types/react-native": "0.70.0", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" }, "repository": { "type": "git", diff --git a/packages/rtn-push-notification/src/apis/addMessageEventListener.ts b/packages/rtn-push-notification/src/apis/addMessageEventListener.ts index 586c7f68a0b..6c27438755d 100644 --- a/packages/rtn-push-notification/src/apis/addMessageEventListener.ts +++ b/packages/rtn-push-notification/src/apis/addMessageEventListener.ts @@ -2,20 +2,20 @@ // SPDX-License-Identifier: Apache-2.0 import { EmitterSubscription } from 'react-native'; -import { nativeEventEmitter } from '../nativeModule'; -import { NativeMessage, PushNotificationMessage } from '../types'; -import { normalizeNativeMessage } from '../utils'; +import { nativeEventEmitter } from '~/src/nativeModule'; +import { NativeMessage, PushNotificationMessage } from '~/src/types'; +import { normalizeNativeMessage } from '~/src/utils'; export const addMessageEventListener = ( event: string, listener: ( message: PushNotificationMessage | null, - completionHandlerId?: string - ) => void + completionHandlerId?: string, + ) => void, ): EmitterSubscription => nativeEventEmitter.addListener(event, (nativeMessage: NativeMessage) => { listener( normalizeNativeMessage(nativeMessage), - nativeMessage.completionHandlerId + nativeMessage.completionHandlerId, ); }); diff --git a/packages/rtn-push-notification/src/apis/addTokenEventListener.ts b/packages/rtn-push-notification/src/apis/addTokenEventListener.ts index afabac89266..f8fc53c206b 100644 --- a/packages/rtn-push-notification/src/apis/addTokenEventListener.ts +++ b/packages/rtn-push-notification/src/apis/addTokenEventListener.ts @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { EmitterSubscription } from 'react-native'; -import { nativeEventEmitter } from '../nativeModule'; -import { TokenPayload } from '../types'; +import { nativeEventEmitter } from '~/src/nativeModule'; +import { TokenPayload } from '~/src/types'; export const addTokenEventListener = ( event: string, - listener: (token: string) => void + listener: (token: string) => void, ): EmitterSubscription => nativeEventEmitter.addListener(event, ({ token }: TokenPayload) => { listener(token); diff --git a/packages/rtn-push-notification/src/apis/completeNotification.ts b/packages/rtn-push-notification/src/apis/completeNotification.ts index b3f31934b57..f287f0bf02a 100644 --- a/packages/rtn-push-notification/src/apis/completeNotification.ts +++ b/packages/rtn-push-notification/src/apis/completeNotification.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; +import { nativeModule } from '~/src/nativeModule'; export const completeNotification = (completionHandlerId: string): void => nativeModule.completeNotification?.(completionHandlerId); diff --git a/packages/rtn-push-notification/src/apis/getBadgeCount.ts b/packages/rtn-push-notification/src/apis/getBadgeCount.ts index b664a4ba1fe..e0dea590d61 100644 --- a/packages/rtn-push-notification/src/apis/getBadgeCount.ts +++ b/packages/rtn-push-notification/src/apis/getBadgeCount.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; +import { nativeModule } from '~/src/nativeModule'; export const getBadgeCount = (): void | Promise => nativeModule.getBadgeCount?.(); diff --git a/packages/rtn-push-notification/src/apis/getConstants.ts b/packages/rtn-push-notification/src/apis/getConstants.ts index edb6b258f55..8a23a9bc65d 100644 --- a/packages/rtn-push-notification/src/apis/getConstants.ts +++ b/packages/rtn-push-notification/src/apis/getConstants.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; +import { nativeModule } from '~/src/nativeModule'; export const getConstants = () => nativeModule.getConstants(); diff --git a/packages/rtn-push-notification/src/apis/getLaunchNotification.ts b/packages/rtn-push-notification/src/apis/getLaunchNotification.ts index 47d5993e0af..2fcba8bcb90 100644 --- a/packages/rtn-push-notification/src/apis/getLaunchNotification.ts +++ b/packages/rtn-push-notification/src/apis/getLaunchNotification.ts @@ -1,12 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; -import { PushNotificationMessage } from '../types'; -import { normalizeNativeMessage } from '../utils'; +import { nativeModule } from '~/src/nativeModule'; +import { PushNotificationMessage } from '~/src/types'; +import { normalizeNativeMessage } from '~/src/utils'; export const getLaunchNotification = async (): Promise => normalizeNativeMessage( - (await nativeModule.getLaunchNotification()) ?? undefined + (await nativeModule.getLaunchNotification()) ?? undefined, ); diff --git a/packages/rtn-push-notification/src/apis/getPermissionStatus.ts b/packages/rtn-push-notification/src/apis/getPermissionStatus.ts index 66d9486cd3d..6c2d8fa674a 100644 --- a/packages/rtn-push-notification/src/apis/getPermissionStatus.ts +++ b/packages/rtn-push-notification/src/apis/getPermissionStatus.ts @@ -1,9 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; -import { PushNotificationPermissionStatus } from '../types'; -import { normalizeNativePermissionStatus } from '../utils'; +import { nativeModule } from '~/src/nativeModule'; +import { PushNotificationPermissionStatus } from '~/src/types'; +import { normalizeNativePermissionStatus } from '~/src/utils'; export const getPermissionStatus = async (): Promise => diff --git a/packages/rtn-push-notification/src/apis/registerHeadlessTask.ts b/packages/rtn-push-notification/src/apis/registerHeadlessTask.ts index 02924f11ef2..90f5ae109aa 100644 --- a/packages/rtn-push-notification/src/apis/registerHeadlessTask.ts +++ b/packages/rtn-push-notification/src/apis/registerHeadlessTask.ts @@ -2,12 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { AppRegistry } from 'react-native'; +import { NativeMessage, PushNotificationMessage } from '~/src/types'; +import { normalizeNativeMessage } from '~/src/utils'; + import { getConstants } from './getConstants'; -import { NativeMessage, PushNotificationMessage } from '../types'; -import { normalizeNativeMessage } from '../utils'; export const registerHeadlessTask = ( - task: (message: PushNotificationMessage | null) => Promise + task: (message: PushNotificationMessage | null) => Promise, ): void => { const { NativeHeadlessTaskKey } = getConstants(); if (NativeHeadlessTaskKey) { @@ -15,7 +16,7 @@ export const registerHeadlessTask = ( NativeHeadlessTaskKey, () => async (nativeMessage: NativeMessage) => { await task(normalizeNativeMessage(nativeMessage)); - } + }, ); } }; diff --git a/packages/rtn-push-notification/src/apis/requestPermissions.ts b/packages/rtn-push-notification/src/apis/requestPermissions.ts index c3bd599013b..db1ebc0afd4 100644 --- a/packages/rtn-push-notification/src/apis/requestPermissions.ts +++ b/packages/rtn-push-notification/src/apis/requestPermissions.ts @@ -1,15 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; -import { PushNotificationPermissions } from '../types'; +import { nativeModule } from '~/src/nativeModule'; +import { PushNotificationPermissions } from '~/src/types'; export const requestPermissions = async ( { alert = true, badge = true, sound = true }: PushNotificationPermissions = { alert: true, badge: true, sound: true, - } + }, ): Promise => nativeModule.requestPermissions({ alert, diff --git a/packages/rtn-push-notification/src/apis/setBadgeCount.ts b/packages/rtn-push-notification/src/apis/setBadgeCount.ts index b287caffffc..4a553233c6e 100644 --- a/packages/rtn-push-notification/src/apis/setBadgeCount.ts +++ b/packages/rtn-push-notification/src/apis/setBadgeCount.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { nativeModule } from '../nativeModule'; +import { nativeModule } from '~/src/nativeModule'; export const setBadgeCount = (count: number): void => nativeModule.setBadgeCount?.(count); diff --git a/packages/rtn-push-notification/src/index.ts b/packages/rtn-push-notification/src/index.ts index fa7318744bf..2e380ee3c3d 100644 --- a/packages/rtn-push-notification/src/index.ts +++ b/packages/rtn-push-notification/src/index.ts @@ -13,6 +13,7 @@ import { requestPermissions, setBadgeCount, } from './apis'; + export { PushNotificationMessage, PushNotificationPermissionStatus, diff --git a/packages/rtn-push-notification/src/nativeModule.ts b/packages/rtn-push-notification/src/nativeModule.ts index ae4ab42b5ea..364e70e4bca 100644 --- a/packages/rtn-push-notification/src/nativeModule.ts +++ b/packages/rtn-push-notification/src/nativeModule.ts @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { NativeModules, NativeEventEmitter } from 'react-native'; +import { NativeEventEmitter, NativeModules } from 'react-native'; + import { LINKING_ERROR } from './constants'; import { PushNotificationNativeModule } from './types'; @@ -14,7 +15,7 @@ export const nativeModule: PushNotificationNativeModule = get() { throw new Error(LINKING_ERROR); }, - } + }, ); export const nativeEventEmitter = new NativeEventEmitter(nativeModule); diff --git a/packages/rtn-push-notification/src/types/native.ts b/packages/rtn-push-notification/src/types/native.ts index 942c3683b4d..4ae20a63b37 100644 --- a/packages/rtn-push-notification/src/types/native.ts +++ b/packages/rtn-push-notification/src/types/native.ts @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { NativeModule } from 'react-native'; + import { PushNotificationMessage, PushNotificationPermissions } from './module'; export interface PushNotificationNativeModule extends NativeModule { - completeNotification?: (completionHandlerId: string) => void; - getConstants: () => { + completeNotification?(completionHandlerId: string): void; + getConstants(): { NativeEvent: { BACKGROUND_MESSAGE_RECEIVED?: string; FOREGROUND_MESSAGE_RECEIVED: string; @@ -16,19 +17,19 @@ export interface PushNotificationNativeModule extends NativeModule { }; NativeHeadlessTaskKey?: string; }; - getLaunchNotification: () => Promise; - getBadgeCount?: () => Promise; - setBadgeCount?: (count: number) => void; - getPermissionStatus: () => Promise; - requestPermissions: ( + getLaunchNotification(): Promise; + getBadgeCount?(): Promise; + setBadgeCount?(count: number): void; + getPermissionStatus(): Promise; + requestPermissions( permissions: PushNotificationPermissions - ) => Promise; + ): Promise; } -export type NativeAction = { +export interface NativeAction { deeplink?: string; url?: string; -}; +} export type NativeMessage = (ApnsMessage | FcmMessage) & { token?: never; @@ -48,7 +49,7 @@ export interface NormalizedValues { } // iOS -export type ApnsMessage = { +export interface ApnsMessage { aps: { alert?: { body?: string; @@ -62,12 +63,12 @@ export type ApnsMessage = { }; rawData?: never; completionHandlerId?: string; -}; +} export type IosPermissionStatus = 'NotDetermined' | 'Authorized' | 'Denied'; // Android -export type FcmMessage = { +export interface FcmMessage { action?: NativeAction; aps?: never; body?: string; @@ -79,7 +80,7 @@ export type FcmMessage = { senderId?: string; sendTime?: number; completionHandlerId?: never; -}; +} export type AndroidPermissionStatus = | 'ShouldRequest' @@ -87,6 +88,6 @@ export type AndroidPermissionStatus = | 'Granted' | 'Denied'; -export type TokenPayload = { +export interface TokenPayload { token: string; -}; +} diff --git a/packages/rtn-push-notification/src/utils/normalizeNativeMessage.ts b/packages/rtn-push-notification/src/utils/normalizeNativeMessage.ts index 93d52f4886b..2ff20d5809e 100644 --- a/packages/rtn-push-notification/src/utils/normalizeNativeMessage.ts +++ b/packages/rtn-push-notification/src/utils/normalizeNativeMessage.ts @@ -9,13 +9,13 @@ import { NativeMessage, NormalizedValues, PushNotificationMessage, -} from '../types'; +} from '~/src/types'; /** * @internal */ export const normalizeNativeMessage = ( - nativeMessage?: NativeMessage + nativeMessage?: NativeMessage, ): PushNotificationMessage | null => { let normalized: NormalizedValues; if (isApnsMessage(nativeMessage)) { @@ -26,6 +26,7 @@ export const normalizeNativeMessage = ( return null; } const { body, imageUrl, title, action, options, data } = normalized; + return { body, data, @@ -42,6 +43,7 @@ const normalizeApnsMessage = (apnsMessage: ApnsMessage): NormalizedValues => { const action = getApnsAction(data?.pinpoint) ?? {}; const imageUrl = data?.['media-url']; const options = getApnsOptions(apnsMessage); + return { body, imageUrl, title, action, options, data }; }; @@ -49,11 +51,12 @@ const normalizeFcmMessage = (fcmMessage: FcmMessage): NormalizedValues => { const { body, imageUrl, rawData: data, title } = fcmMessage; const action = getFcmAction(fcmMessage.action) ?? {}; const options = getFcmOptions(fcmMessage); + return { body, imageUrl, title, action, options, data }; }; const getApnsAction = ( - action?: NativeAction + action?: NativeAction, ): Pick | undefined => { if (action?.deeplink) { return { deeplinkUrl: action.deeplink }; @@ -61,7 +64,7 @@ const getApnsAction = ( }; const getFcmAction = ( - action?: NativeAction + action?: NativeAction, ): Pick | undefined => { if (action?.url) { return { goToUrl: action.url }; @@ -76,6 +79,7 @@ const getApnsOptions = ({ }: ApnsMessage): Pick => { const { subtitle } = aps.alert ?? {}; const apnsOptions = { ...(subtitle && { subtitle }) }; + return { ...(!isEmpty(apnsOptions) && { apnsOptions }) }; }; @@ -91,13 +95,14 @@ const getFcmOptions = ({ senderId, sendTime: new Date(sendTime), }; + return { ...(!isEmpty(fcmOptions) && { fcmOptions }) }; }; const isApnsMessage = ( - nativeMessage?: NativeMessage + nativeMessage?: NativeMessage, ): nativeMessage is ApnsMessage => !!nativeMessage?.aps; const isFcmMessage = ( - nativeMessage?: NativeMessage + nativeMessage?: NativeMessage, ): nativeMessage is FcmMessage => !!nativeMessage?.rawData; diff --git a/packages/rtn-push-notification/src/utils/normalizeNativePermissionStatus.ts b/packages/rtn-push-notification/src/utils/normalizeNativePermissionStatus.ts index eaf6f7c2f0e..e152d1129bf 100644 --- a/packages/rtn-push-notification/src/utils/normalizeNativePermissionStatus.ts +++ b/packages/rtn-push-notification/src/utils/normalizeNativePermissionStatus.ts @@ -4,13 +4,13 @@ import { NativePermissionStatus, PushNotificationPermissionStatus, -} from '../types'; +} from '~/src/types'; /** * @internal */ export const normalizeNativePermissionStatus = ( - nativeStatus: NativePermissionStatus + nativeStatus: NativePermissionStatus, ): PushNotificationPermissionStatus => { switch (nativeStatus) { case 'ShouldRequest': diff --git a/packages/rtn-push-notification/tsconfig.build.json b/packages/rtn-push-notification/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/rtn-push-notification/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/rtn-push-notification/tsconfig.json b/packages/rtn-push-notification/tsconfig.json index b0bed2b668d..cfd3c46cdf0 100755 --- a/packages/rtn-push-notification/tsconfig.json +++ b/packages/rtn-push-notification/tsconfig.json @@ -1,9 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { + "rootDirs": ["src"], + "baseUrl": ".", "importHelpers": false, - "strict": true, - "noImplicitAny": true + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/rtn-push-notification/tsconfig.test.json b/packages/rtn-push-notification/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/rtn-push-notification/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/rtn-push-notification/tsconfig.watch.json b/packages/rtn-push-notification/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/rtn-push-notification/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/rtn-push-notification/tslint.json b/packages/rtn-push-notification/tslint.json deleted file mode 100644 index 8eafab1d2b4..00000000000 --- a/packages/rtn-push-notification/tslint.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/rtn-web-browser/.eslintrc.js b/packages/rtn-web-browser/.eslintrc.js new file mode 100644 index 00000000000..d95a8830feb --- /dev/null +++ b/packages/rtn-web-browser/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/rtn-web-browser', + }, + ], + }, +}; diff --git a/packages/rtn-web-browser/jest.config.js b/packages/rtn-web-browser/jest.config.js deleted file mode 100644 index 3a0e3340f9f..00000000000 --- a/packages/rtn-web-browser/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100, - }, - }, -}; diff --git a/packages/rtn-web-browser/jest.config.mjs b/packages/rtn-web-browser/jest.config.mjs new file mode 100644 index 00000000000..7c9c9dd41cb --- /dev/null +++ b/packages/rtn-web-browser/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: 100, + }, + }, +}); + +export default jestConfig; diff --git a/packages/rtn-web-browser/package.json b/packages/rtn-web-browser/package.json index 4d7ea1b41cc..d1fcedcbbe2 100644 --- a/packages/rtn-web-browser/package.json +++ b/packages/rtn-web-browser/package.json @@ -11,24 +11,24 @@ "access": "public" }, "scripts": { - "test": "tslint 'src/**/*.ts'", + "test": "npm run lint", "test:android": "./android/gradlew test -p ./android", "build-with-test": "npm run clean && npm test && tsc", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs", "clean": "rimraf dist lib lib-esm", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.build.json -t 88.21" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 88.21" }, "devDependencies": { "@rollup/plugin-typescript": "11.1.5", "@types/react-native": "0.70.0", "react-native": "0.72.3", - "rollup": "3.29.4", - "typescript": "5.1.6" + "rollup": "3.29.4" }, "repository": { "type": "git", diff --git a/packages/rtn-web-browser/src/apis/openAuthSessionAsync.ts b/packages/rtn-web-browser/src/apis/openAuthSessionAsync.ts index 37f5a287839..9519f9ef056 100644 --- a/packages/rtn-web-browser/src/apis/openAuthSessionAsync.ts +++ b/packages/rtn-web-browser/src/apis/openAuthSessionAsync.ts @@ -7,7 +7,7 @@ import { NativeEventSubscription, Platform, } from 'react-native'; -import { nativeModule } from '../nativeModule'; +import { nativeModule } from '~/src/nativeModule'; let appStateListener: NativeEventSubscription | undefined; let redirectListener: NativeEventSubscription | undefined; @@ -15,7 +15,7 @@ let redirectListener: NativeEventSubscription | undefined; export const openAuthSessionAsync = async ( url: string, redirectUrls: string[], - prefersEphemeralSession?: boolean + prefersEphemeralSession?: boolean, ) => { // enforce HTTPS const httpsUrl = url.replace('http://', 'https://'); @@ -31,16 +31,17 @@ export const openAuthSessionAsync = async ( const openAuthSessionIOS = async ( url: string, redirectUrls: string[], - prefersEphemeralSession: boolean = false + prefersEphemeralSession = false, ) => { const redirectUrl = redirectUrls.find( // take the first non-web url as the deeplink - item => !item.startsWith('https://') && !item.startsWith('http://') + item => !item.startsWith('https://') && !item.startsWith('http://'), ); + return nativeModule.openAuthSessionAsync( url, redirectUrl, - prefersEphemeralSession + prefersEphemeralSession, ); }; @@ -56,6 +57,7 @@ const openAuthSessionAndroid = async (url: string, redirectUrls: string[]) => { // open chrome tab nativeModule.openAuthSessionAsync(url), ]); + return redirectUrl; } finally { appStateListener?.remove(); diff --git a/packages/rtn-web-browser/src/nativeModule.ts b/packages/rtn-web-browser/src/nativeModule.ts index 9ec0ce26227..8bf4aee643a 100644 --- a/packages/rtn-web-browser/src/nativeModule.ts +++ b/packages/rtn-web-browser/src/nativeModule.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { NativeModules } from 'react-native'; + import { LINKING_ERROR } from './constants'; import { WebBrowserNativeModule } from './types'; @@ -14,5 +15,5 @@ export const nativeModule: WebBrowserNativeModule = get() { throw new Error(LINKING_ERROR); }, - } + }, ); diff --git a/packages/rtn-web-browser/src/types/native.ts b/packages/rtn-web-browser/src/types/native.ts index 3ddc1a6d952..01d3ffb9a22 100644 --- a/packages/rtn-web-browser/src/types/native.ts +++ b/packages/rtn-web-browser/src/types/native.ts @@ -4,9 +4,9 @@ import { NativeModule } from 'react-native'; export interface WebBrowserNativeModule extends NativeModule { - openAuthSessionAsync: ( + openAuthSessionAsync( url: string, redirectUrl?: string, prefersEphemeralSession?: boolean - ) => Promise; + ): Promise; } diff --git a/packages/rtn-web-browser/tsconfig.build.json b/packages/rtn-web-browser/tsconfig.build.json new file mode 100644 index 00000000000..93dc82e4dc9 --- /dev/null +++ b/packages/rtn-web-browser/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["__tests__"] +} diff --git a/packages/rtn-web-browser/tsconfig.json b/packages/rtn-web-browser/tsconfig.json index b0bed2b668d..cfd3c46cdf0 100755 --- a/packages/rtn-web-browser/tsconfig.json +++ b/packages/rtn-web-browser/tsconfig.json @@ -1,9 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { + "rootDirs": ["src"], + "baseUrl": ".", "importHelpers": false, - "strict": true, - "noImplicitAny": true + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/rtn-web-browser/tsconfig.test.json b/packages/rtn-web-browser/tsconfig.test.json new file mode 100644 index 00000000000..ea6be8e9a50 --- /dev/null +++ b/packages/rtn-web-browser/tsconfig.test.json @@ -0,0 +1,3 @@ +{ + "extends": "./tsconfig.json" +} diff --git a/packages/rtn-web-browser/tsconfig.watch.json b/packages/rtn-web-browser/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/rtn-web-browser/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/rtn-web-browser/tslint.json b/packages/rtn-web-browser/tslint.json deleted file mode 100644 index 8eafab1d2b4..00000000000 --- a/packages/rtn-web-browser/tslint.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [true, 120], - "no-empty-interface": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "never", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [ - true, - "always", - "ignore-interfaces", - "ignore-bound-class-methods" - ] - }, - "rulesDirectory": [] -} diff --git a/packages/storage/.eslintrc.js b/packages/storage/.eslintrc.js new file mode 100644 index 00000000000..6e4fa12792c --- /dev/null +++ b/packages/storage/.eslintrc.js @@ -0,0 +1,14 @@ +/** @type {import("eslint").ESLint.ConfigData}*/ +module.exports = { + extends: '../../.eslintrc.js', + rules: { + 'no-relative-import-paths/no-relative-import-paths': [ + 'error', + { + allowSameFolder: true, + prefix: '~', + rootDir: 'packages/storage', + }, + ], + }, +}; diff --git a/packages/storage/jest.config.js b/packages/storage/jest.config.js deleted file mode 100644 index 06b4ec635ed..00000000000 --- a/packages/storage/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - ...require('../../jest.config'), - coverageThreshold: { - global: { - branches: 75, - functions: 80, - lines: 81, - statements: 91, - }, - }, -}; diff --git a/packages/storage/jest.config.mjs b/packages/storage/jest.config.mjs new file mode 100644 index 00000000000..aa6d23adb5f --- /dev/null +++ b/packages/storage/jest.config.mjs @@ -0,0 +1,15 @@ +import { getJestConfig } from '../../jest/getJestConfig.mjs'; +import tsconfig from './tsconfig.json' assert { type: 'json' }; + +const jestConfig = getJestConfig(tsconfig.compilerOptions, { + coverageThreshold: { + global: { + branches: 75, + functions: 80, + lines: 81, + statements: 90, + }, + }, +}); + +export default jestConfig; diff --git a/packages/storage/package.json b/packages/storage/package.json index d1eac96c780..aa8737a616c 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -22,18 +22,19 @@ "access": "public" }, "scripts": { - "test": "npm run lint && jest -w 1 --coverage --logHeapUsage", + "test": "npm run lint && cross-env NODE_OPTIONS='--no-warnings' jest -w 1 --coverage --logHeapUsage", "build-with-test": "npm test && npm run build", "build:umd": "webpack && webpack --config ./webpack.config.dev.js", "build:esm-cjs": "rollup -c rollup.config.mjs", - "build:cjs:watch": "rimraf dist/cjs && tsc -m commonjs --outDir dist/cjs --watch", - "build:esm:watch": "rimraf dist/esm && tsc -m esnext --outDir dist/esm --watch", + "build:cjs:watch": "rimraf dist/cjs && tsc -p tsconfig.watch.json -m commonjs --outDir dist/cjs --watch", + "build:esm:watch": "rimraf dist/esm && tsc -p tsconfig.watch.json -m esnext --outDir dist/esm --watch", "build": "npm run clean && npm run build:esm-cjs && npm run build:umd", "clean": "npm run clean:size && rimraf lib-esm lib dist", "clean:size": "rimraf dual-publish-tmp tmp*", "format": "echo \"Not implemented\"", - "lint": "tslint 'src/**/*.ts' && npm run ts-coverage", - "ts-coverage": "typescript-coverage-report -p ./tsconfig.build.json -t 90.31" + "lint": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js && npm run ts-coverage", + "lint:fix": "eslint '**/*.{ts,tsx}' -c ../../.eslintrc.scoped.js --fix", + "ts-coverage": "rimraf coverage-ts && typescript-coverage-report -p ./tsconfig.build.json -t 90.31" }, "typesVersions": { ">=4.2": { @@ -105,7 +106,6 @@ "@aws-amplify/react-native": "1.0.8", "@rollup/plugin-json": "6.0.1", "@rollup/plugin-typescript": "11.1.5", - "rollup": "3.29.4", - "typescript": "5.0.2" + "rollup": "3.29.4" } } diff --git a/packages/storage/src/errors/CanceledError.ts b/packages/storage/src/errors/CanceledError.ts index 8303aa86751..9388653432a 100644 --- a/packages/storage/src/errors/CanceledError.ts +++ b/packages/storage/src/errors/CanceledError.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyErrorParams } from '@aws-amplify/core/internals/utils'; + import { StorageError } from './StorageError'; /** diff --git a/packages/storage/src/errors/utils/assertValidationError.ts b/packages/storage/src/errors/utils/assertValidationError.ts index aab418634a2..78349841144 100644 --- a/packages/storage/src/errors/utils/assertValidationError.ts +++ b/packages/storage/src/errors/utils/assertValidationError.ts @@ -4,12 +4,12 @@ import { StorageValidationErrorCode, validationErrorMap, -} from '../types/validation'; -import { StorageError } from '../StorageError'; +} from '~/src/errors/types/validation'; +import { StorageError } from '~/src/errors/StorageError'; export function assertValidationError( assertion: boolean, - name: StorageValidationErrorCode + name: StorageValidationErrorCode, ): asserts assertion { const { message, recoverySuggestion } = validationErrorMap[name]; diff --git a/packages/storage/src/providers/s3/apis/copy.ts b/packages/storage/src/providers/s3/apis/copy.ts index 1eed73e3972..e4c4576639b 100644 --- a/packages/storage/src/providers/s3/apis/copy.ts +++ b/packages/storage/src/providers/s3/apis/copy.ts @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { CopyInput, CopyOutput, S3Exception } from '../types'; +import { CopyInput, CopyOutput, S3Exception } from '~/src/providers/s3/types'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; + import { copy as copyInternal } from './internal/copy'; -import { StorageValidationErrorCode } from '../../../errors/types/validation'; /** * Copy an object from a source object to a new object within the same bucket. Can optionally copy files across diff --git a/packages/storage/src/providers/s3/apis/downloadData.ts b/packages/storage/src/providers/s3/apis/downloadData.ts index 281f73a7456..a3b1a64c4d4 100644 --- a/packages/storage/src/providers/s3/apis/downloadData.ts +++ b/packages/storage/src/providers/s3/apis/downloadData.ts @@ -3,14 +3,17 @@ import { Amplify } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; - -import { DownloadDataInput, DownloadDataOutput, S3Exception } from '../types'; -import { resolveS3ConfigAndInput } from '../utils/resolveS3ConfigAndInput'; -import { StorageValidationErrorCode } from '../../../errors/types/validation'; -import { createDownloadTask } from '../utils'; -import { getObject } from '../utils/client'; -import { getStorageUserAgentValue } from '../utils/userAgent'; -import { logger } from '../../../utils'; +import { + DownloadDataInput, + DownloadDataOutput, + S3Exception, +} from '~/src/providers/s3/types'; +import { resolveS3ConfigAndInput } from '~/src/providers/s3/utils/resolveS3ConfigAndInput'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; +import { createDownloadTask } from '~/src/providers/s3/utils'; +import { getObject } from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; +import { logger } from '~/src/utils'; /** * Download S3 object data to memory @@ -51,18 +54,19 @@ export const downloadData = (input: DownloadDataInput): DownloadDataOutput => { abortController.abort(message); }, }); + return downloadTask; }; const downloadDataJob = ( { options: downloadDataOptions, key }: DownloadDataInput, - abortSignal: AbortSignal + abortSignal: AbortSignal, ) => async () => { const { bucket, keyPrefix, s3Config } = await resolveS3ConfigAndInput( Amplify, - downloadDataOptions + downloadDataOptions, ); const finalKey = keyPrefix + key; @@ -89,8 +93,9 @@ const downloadDataJob = ...(downloadDataOptions?.bytesRange && { Range: `bytes=${downloadDataOptions.bytesRange.start}-${downloadDataOptions.bytesRange.end}`, }), - } + }, ); + return { key, body, diff --git a/packages/storage/src/providers/s3/apis/getProperties.ts b/packages/storage/src/providers/s3/apis/getProperties.ts index 7f012d3f2be..4b4d107cd75 100644 --- a/packages/storage/src/providers/s3/apis/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/getProperties.ts @@ -2,7 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { GetPropertiesOutput, GetPropertiesInput, S3Exception } from '../types'; +import { + GetPropertiesInput, + GetPropertiesOutput, + S3Exception, +} from '~/src/providers/s3/types'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; + import { getProperties as getPropertiesInternal } from './internal/getProperties'; /** @@ -15,7 +21,7 @@ import { getProperties as getPropertiesInternal } from './internal/getProperties * @throws A {@link StorageValidationErrorCode} when API call parameters are invalid. */ export const getProperties = ( - input: GetPropertiesInput + input: GetPropertiesInput, ): Promise => { return getPropertiesInternal(Amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/getUrl.ts b/packages/storage/src/providers/s3/apis/getUrl.ts index 0ceb612c06c..55805628742 100644 --- a/packages/storage/src/providers/s3/apis/getUrl.ts +++ b/packages/storage/src/providers/s3/apis/getUrl.ts @@ -2,8 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { StorageValidationErrorCode } from '../../../errors/types/validation'; -import { GetUrlInput, GetUrlOutput, S3Exception } from '../types'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; +import { + GetUrlInput, + GetUrlOutput, + S3Exception, +} from '~/src/providers/s3/types'; +import { StorageError } from '~/src/errors/StorageError'; + import { getUrl as getUrlInternal } from './internal/getUrl'; /** diff --git a/packages/storage/src/providers/s3/apis/internal/copy.ts b/packages/storage/src/providers/s3/apis/internal/copy.ts index 47afbc9107e..9a95ac2572d 100644 --- a/packages/storage/src/providers/s3/apis/internal/copy.ts +++ b/packages/storage/src/providers/s3/apis/internal/copy.ts @@ -3,17 +3,17 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; -import { CopyInput, CopyOutput } from '../../types'; -import { resolveS3ConfigAndInput } from '../../utils'; -import { StorageValidationErrorCode } from '../../../../errors/types/validation'; -import { assertValidationError } from '../../../../errors/utils/assertValidationError'; -import { copyObject } from '../../utils/client'; -import { getStorageUserAgentValue } from '../../utils/userAgent'; -import { logger } from '../../../../utils'; +import { CopyInput, CopyOutput } from '~/src/providers/s3/types'; +import { resolveS3ConfigAndInput } from '~/src/providers/s3/utils'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { copyObject } from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; +import { logger } from '~/src/utils'; export const copy = async ( amplify: AmplifyClassV6, - input: CopyInput + input: CopyInput, ): Promise => { const { source: { key: sourceKey }, @@ -23,7 +23,7 @@ export const copy = async ( assertValidationError(!!sourceKey, StorageValidationErrorCode.NoSourceKey); assertValidationError( !!destinationKey, - StorageValidationErrorCode.NoDestinationKey + StorageValidationErrorCode.NoDestinationKey, ); const { @@ -33,7 +33,7 @@ export const copy = async ( } = await resolveS3ConfigAndInput(amplify, input.source); const { keyPrefix: destinationKeyPrefix } = await resolveS3ConfigAndInput( amplify, - input.destination + input.destination, ); // resolveS3ConfigAndInput does not make extra API calls or storage access if called repeatedly. // TODO(ashwinkumar6) V6-logger: warn `You may copy files from another user if the source level is "protected", currently it's ${srcLevel}` @@ -50,7 +50,7 @@ export const copy = async ( CopySource: finalCopySource, Key: finalCopyDestination, MetadataDirective: 'COPY', // Copies over metadata like contentType as well - } + }, ); return { diff --git a/packages/storage/src/providers/s3/apis/internal/getProperties.ts b/packages/storage/src/providers/s3/apis/internal/getProperties.ts index cd75852b38f..6cd7f5b8511 100644 --- a/packages/storage/src/providers/s3/apis/internal/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/internal/getProperties.ts @@ -3,21 +3,24 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; -import { GetPropertiesInput, GetPropertiesOutput } from '../../types'; -import { resolveS3ConfigAndInput } from '../../utils'; -import { headObject } from '../../utils/client'; -import { getStorageUserAgentValue } from '../../utils/userAgent'; -import { logger } from '../../../../utils'; +import { + GetPropertiesInput, + GetPropertiesOutput, +} from '~/src/providers/s3/types'; +import { resolveS3ConfigAndInput } from '~/src/providers/s3/utils'; +import { headObject } from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; +import { logger } from '~/src/utils'; -export const getProperties = async function ( +export const getProperties = async ( amplify: AmplifyClassV6, input: GetPropertiesInput, - action?: StorageAction -): Promise { + action?: StorageAction, +): Promise => { const { key, options } = input; const { s3Config, bucket, keyPrefix } = await resolveS3ConfigAndInput( amplify, - options + options, ); const finalKey = `${keyPrefix}${key}`; @@ -26,14 +29,15 @@ export const getProperties = async function ( { ...s3Config, userAgentValue: getStorageUserAgentValue( - action ?? StorageAction.GetProperties + action ?? StorageAction.GetProperties, ), }, { Bucket: bucket, Key: finalKey, - } + }, ); + return { key, contentType: response.ContentType, diff --git a/packages/storage/src/providers/s3/apis/internal/getUrl.ts b/packages/storage/src/providers/s3/apis/internal/getUrl.ts index 5f6fb0ed81d..b682e279c8d 100644 --- a/packages/storage/src/providers/s3/apis/internal/getUrl.ts +++ b/packages/storage/src/providers/s3/apis/internal/getUrl.ts @@ -2,22 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6 } from '@aws-amplify/core'; -import { GetUrlInput, GetUrlOutput } from '../../types'; -import { StorageValidationErrorCode } from '../../../../errors/types/validation'; -import { getPresignedGetObjectUrl } from '../../utils/client'; -import { getProperties } from './getProperties'; -import { resolveS3ConfigAndInput } from '../../utils'; -import { assertValidationError } from '../../../../errors/utils/assertValidationError'; +import { GetUrlInput, GetUrlOutput } from '~/src/providers/s3/types'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; +import { getPresignedGetObjectUrl } from '~/src/providers/s3/utils/client'; +import { resolveS3ConfigAndInput } from '~/src/providers/s3/utils'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; import { DEFAULT_PRESIGN_EXPIRATION, MAX_URL_EXPIRATION, -} from '../../utils/constants'; +} from '~/src/providers/s3/utils/constants'; import { StorageAction } from '@aws-amplify/core/internals/utils'; -export const getUrl = async function ( +import { getProperties } from './getProperties'; + +export const getUrl = async ( amplify: AmplifyClassV6, - input: GetUrlInput -): Promise { + input: GetUrlInput, +): Promise => { const { key, options } = input; if (options?.validateObjectExistence) { @@ -26,21 +27,21 @@ export const getUrl = async function ( const { s3Config, keyPrefix, bucket } = await resolveS3ConfigAndInput( amplify, - options + options, ); let urlExpirationInSec = options?.expiresIn ?? DEFAULT_PRESIGN_EXPIRATION; const awsCredExpiration = s3Config.credentials?.expiration; if (awsCredExpiration) { const awsCredExpirationInSec = Math.floor( - (awsCredExpiration.getTime() - Date.now()) / 1000 + (awsCredExpiration.getTime() - Date.now()) / 1000, ); urlExpirationInSec = Math.min(awsCredExpirationInSec, urlExpirationInSec); } const maxUrlExpirationInSec = MAX_URL_EXPIRATION / 1000; assertValidationError( urlExpirationInSec <= maxUrlExpirationInSec, - StorageValidationErrorCode.UrlExpirationMaxLimitExceed + StorageValidationErrorCode.UrlExpirationMaxLimitExceed, ); // expiresAt is the minimum of credential expiration and url expiration @@ -53,7 +54,7 @@ export const getUrl = async function ( { Bucket: bucket, Key: `${keyPrefix}${key}`, - } + }, ), expiresAt: new Date(Date.now() + urlExpirationInSec * 1000), }; diff --git a/packages/storage/src/providers/s3/apis/internal/list.ts b/packages/storage/src/providers/s3/apis/internal/list.ts index 1e90565e6a9..b7e4be22426 100644 --- a/packages/storage/src/providers/s3/apis/internal/list.ts +++ b/packages/storage/src/providers/s3/apis/internal/list.ts @@ -5,32 +5,32 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; import { ListAllInput, - ListPaginateInput, ListAllOutput, - ListPaginateOutput, ListOutputItem, -} from '../../types'; -import { resolveS3ConfigAndInput } from '../../utils'; -import { ResolvedS3Config } from '../../types/options'; + ListPaginateInput, + ListPaginateOutput, +} from '~/src/providers/s3/types'; +import { resolveS3ConfigAndInput } from '~/src/providers/s3/utils'; +import { ResolvedS3Config } from '~/src/providers/s3/types/options'; import { - listObjectsV2, ListObjectsV2Input, ListObjectsV2Output, -} from '../../utils/client'; -import { getStorageUserAgentValue } from '../../utils/userAgent'; -import { logger } from '../../../../utils'; + listObjectsV2, +} from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; +import { logger } from '~/src/utils'; const MAX_PAGE_SIZE = 1000; -type ListInputArgs = { +interface ListInputArgs { s3Config: ResolvedS3Config; listParams: ListObjectsV2Input; prefix: string; -}; +} export const list = async ( amplify: AmplifyClassV6, - input?: ListAllInput | ListPaginateInput + input?: ListAllInput | ListPaginateInput, ): Promise => { const { options = {}, prefix: path = '' } = input ?? {}; const { @@ -44,7 +44,7 @@ export const list = async ( logger.debug( `listAll is set to true, ignoring ${ anyOptions?.pageSize ? `pageSize: ${anyOptions?.pageSize}` : '' - } ${anyOptions?.nextToken ? `nextToken: ${anyOptions?.nextToken}` : ''}.` + } ${anyOptions?.nextToken ? `nextToken: ${anyOptions?.nextToken}` : ''}.`, ); } const listParams = { @@ -54,9 +54,10 @@ export const list = async ( ContinuationToken: options?.listAll ? undefined : options?.nextToken, }; logger.debug(`listing items from "${listParams.Prefix}"`); + return options.listAll - ? await _listAll({ s3Config, listParams, prefix }) - : await _list({ s3Config, listParams, prefix }); + ? _listAll({ s3Config, listParams, prefix }) + : _list({ s3Config, listParams, prefix }); }; const _listAll = async ({ @@ -101,7 +102,7 @@ const _list = async ({ ...s3Config, userAgentValue: getStorageUserAgentValue(StorageAction.List), }, - listParamsClone + listParamsClone, ); if (!response?.Contents) { @@ -116,6 +117,7 @@ const _list = async ({ lastModified: item.LastModified, size: item.Size, })); + return { items: listResult, nextToken: response.NextContinuationToken, diff --git a/packages/storage/src/providers/s3/apis/internal/remove.ts b/packages/storage/src/providers/s3/apis/internal/remove.ts index f942f1a180a..d78c8f885a5 100644 --- a/packages/storage/src/providers/s3/apis/internal/remove.ts +++ b/packages/storage/src/providers/s3/apis/internal/remove.ts @@ -3,20 +3,20 @@ import { AmplifyClassV6 } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; -import { RemoveInput, RemoveOutput } from '../../types'; -import { resolveS3ConfigAndInput } from '../../utils'; -import { deleteObject } from '../../utils/client'; -import { getStorageUserAgentValue } from '../../utils/userAgent'; -import { logger } from '../../../../utils'; +import { RemoveInput, RemoveOutput } from '~/src/providers/s3/types'; +import { resolveS3ConfigAndInput } from '~/src/providers/s3/utils'; +import { deleteObject } from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; +import { logger } from '~/src/utils'; export const remove = async ( amplify: AmplifyClassV6, - input: RemoveInput + input: RemoveInput, ): Promise => { const { key, options = {} } = input; const { s3Config, keyPrefix, bucket } = await resolveS3ConfigAndInput( amplify, - options + options, ); const finalKey = `${keyPrefix}${key}`; @@ -29,8 +29,9 @@ export const remove = async ( { Bucket: bucket, Key: finalKey, - } + }, ); + return { key, }; diff --git a/packages/storage/src/providers/s3/apis/list.ts b/packages/storage/src/providers/s3/apis/list.ts index a1a7f3e5458..8c2d6ee8964 100644 --- a/packages/storage/src/providers/s3/apis/list.ts +++ b/packages/storage/src/providers/s3/apis/list.ts @@ -4,13 +4,16 @@ import { Amplify } from '@aws-amplify/core'; import { ListAllInput, - ListPaginateInput, ListAllOutput, + ListPaginateInput, ListPaginateOutput, -} from '../types'; + S3Exception, +} from '~/src/providers/s3/types'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; + import { list as listInternal } from './internal/list'; -type ListApi = { +interface ListApi { /** * List files with given prefix in pages * pageSize defaulted to 1000. Additionally, the result will include a nextToken if there are more items to retrieve. @@ -28,10 +31,10 @@ type ListApi = { * @throws validation: {@link StorageValidationErrorCode } - thrown when there are issues with credentials */ (input?: ListAllInput): Promise; -}; +} export const list: ListApi = ( - input?: ListAllInput | ListPaginateInput + input?: ListAllInput | ListPaginateInput, ): Promise => { return listInternal(Amplify, input ?? {}); }; diff --git a/packages/storage/src/providers/s3/apis/remove.ts b/packages/storage/src/providers/s3/apis/remove.ts index d26fb3058c5..adf8476f504 100644 --- a/packages/storage/src/providers/s3/apis/remove.ts +++ b/packages/storage/src/providers/s3/apis/remove.ts @@ -2,7 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { Amplify } from '@aws-amplify/core'; -import { RemoveInput, RemoveOutput } from '../types'; +import { + RemoveInput, + RemoveOutput, + S3Exception, +} from '~/src/providers/s3/types'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; + import { remove as removeInternal } from './internal/remove'; /** diff --git a/packages/storage/src/providers/s3/apis/server/copy.ts b/packages/storage/src/providers/s3/apis/server/copy.ts index ea8e9de90f8..d2115f0699e 100644 --- a/packages/storage/src/providers/s3/apis/server/copy.ts +++ b/packages/storage/src/providers/s3/apis/server/copy.ts @@ -5,12 +5,12 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { CopyInput, CopyOutput } from '../../types'; -import { copy as copyInternal } from '../internal/copy'; +import { CopyInput, CopyOutput } from '~/src/providers/s3/types'; +import { copy as copyInternal } from '~/src/providers/s3/apis/internal/copy'; export const copy = async ( contextSpec: AmplifyServer.ContextSpec, - input: CopyInput + input: CopyInput, ): Promise => { return copyInternal(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/server/getProperties.ts b/packages/storage/src/providers/s3/apis/server/getProperties.ts index c26bf24f502..4008a6a5f40 100644 --- a/packages/storage/src/providers/s3/apis/server/getProperties.ts +++ b/packages/storage/src/providers/s3/apis/server/getProperties.ts @@ -5,15 +5,18 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { GetPropertiesInput, GetPropertiesOutput } from '../../types'; -import { getProperties as getPropertiesInternal } from '../internal/getProperties'; +import { + GetPropertiesInput, + GetPropertiesOutput, +} from '~/src/providers/s3/types'; +import { getProperties as getPropertiesInternal } from '~/src/providers/s3/apis/internal/getProperties'; export const getProperties = ( contextSpec: AmplifyServer.ContextSpec, - input: GetPropertiesInput + input: GetPropertiesInput, ): Promise => { return getPropertiesInternal( getAmplifyServerContext(contextSpec).amplify, - input + input, ); }; diff --git a/packages/storage/src/providers/s3/apis/server/getUrl.ts b/packages/storage/src/providers/s3/apis/server/getUrl.ts index 7b6cc802cbe..ff88fef22bd 100644 --- a/packages/storage/src/providers/s3/apis/server/getUrl.ts +++ b/packages/storage/src/providers/s3/apis/server/getUrl.ts @@ -5,12 +5,12 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { GetUrlInput, GetUrlOutput } from '../../types'; -import { getUrl as getUrlInternal } from '../internal/getUrl'; +import { GetUrlInput, GetUrlOutput } from '~/src/providers/s3/types'; +import { getUrl as getUrlInternal } from '~/src/providers/s3/apis/internal/getUrl'; export const getUrl = async ( contextSpec: AmplifyServer.ContextSpec, - input: GetUrlInput + input: GetUrlInput, ): Promise => { return getUrlInternal(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/server/list.ts b/packages/storage/src/providers/s3/apis/server/list.ts index 970301c1622..5427d19b257 100644 --- a/packages/storage/src/providers/s3/apis/server/list.ts +++ b/packages/storage/src/providers/s3/apis/server/list.ts @@ -7,13 +7,15 @@ import { } from '@aws-amplify/core/internals/adapter-core'; import { ListAllInput, - ListPaginateInput, ListAllOutput, + ListPaginateInput, ListPaginateOutput, -} from '../../types'; -import { list as listInternal } from '../internal/list'; + S3Exception, +} from '~/src/providers/s3/types'; +import { list as listInternal } from '~/src/providers/s3/apis/internal/list'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; -type ListApi = { +interface ListApi { /** * Lists bucket objects with pagination. * @param {ListPaginateInput} The input object @@ -24,7 +26,7 @@ type ListApi = { */ ( contextSpec: AmplifyServer.ContextSpec, - input?: ListPaginateInput + input?: ListPaginateInput, ): Promise; /** * Lists all bucket objects. @@ -35,16 +37,16 @@ type ListApi = { */ ( contextSpec: AmplifyServer.ContextSpec, - input?: ListAllInput + input?: ListAllInput, ): Promise; -}; +} export const list: ListApi = ( contextSpec: AmplifyServer.ContextSpec, - input?: ListAllInput | ListPaginateInput + input?: ListAllInput | ListPaginateInput, ): Promise => { return listInternal( getAmplifyServerContext(contextSpec).amplify, - input ?? {} + input ?? {}, ); }; diff --git a/packages/storage/src/providers/s3/apis/server/remove.ts b/packages/storage/src/providers/s3/apis/server/remove.ts index d54d0687333..42eb15680c0 100644 --- a/packages/storage/src/providers/s3/apis/server/remove.ts +++ b/packages/storage/src/providers/s3/apis/server/remove.ts @@ -5,12 +5,12 @@ import { AmplifyServer, getAmplifyServerContext, } from '@aws-amplify/core/internals/adapter-core'; -import { RemoveInput, RemoveOutput } from '../../types'; -import { remove as removeInternal } from '../internal/remove'; +import { RemoveInput, RemoveOutput } from '~/src/providers/s3/types'; +import { remove as removeInternal } from '~/src/providers/s3/apis/internal/remove'; export const remove = ( contextSpec: AmplifyServer.ContextSpec, - input: RemoveInput + input: RemoveInput, ): Promise => { return removeInternal(getAmplifyServerContext(contextSpec).amplify, input); }; diff --git a/packages/storage/src/providers/s3/apis/uploadData/byteLength.ts b/packages/storage/src/providers/s3/apis/uploadData/byteLength.ts index 96b97f42430..9b6ea87e42f 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/byteLength.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/byteLength.ts @@ -25,6 +25,7 @@ export const byteLength = (input?: any): number | undefined => { // handles browser File object return input.size; } + // TODO: support Node.js stream size when Node.js runtime is supported out-of-box. return undefined; }; diff --git a/packages/storage/src/providers/s3/apis/uploadData/index.ts b/packages/storage/src/providers/s3/apis/uploadData/index.ts index ad43972d46b..ca28d656b49 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/index.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/index.ts @@ -1,11 +1,19 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { UploadDataInput, UploadDataOutput, S3Exception } from '../../types'; -import { createUploadTask } from '../../utils'; -import { assertValidationError } from '../../../../errors/utils/assertValidationError'; -import { StorageValidationErrorCode } from '../../../../errors/types/validation'; -import { DEFAULT_PART_SIZE, MAX_OBJECT_SIZE } from '../../utils/constants'; +import { + S3Exception, + UploadDataInput, + UploadDataOutput, +} from '~/src/providers/s3/types'; +import { createUploadTask } from '~/src/providers/s3/utils'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; +import { + DEFAULT_PART_SIZE, + MAX_OBJECT_SIZE, +} from '~/src/providers/s3/utils/constants'; + import { byteLength } from './byteLength'; import { putObjectJob } from './putObjectJob'; import { getMultipartUploadHandlers } from './multipart'; @@ -64,11 +72,12 @@ export const uploadData = (input: UploadDataInput): UploadDataOutput => { const dataByteLength = byteLength(data); assertValidationError( dataByteLength === undefined || dataByteLength <= MAX_OBJECT_SIZE, - StorageValidationErrorCode.ObjectIsTooLarge + StorageValidationErrorCode.ObjectIsTooLarge, ); if (dataByteLength && dataByteLength <= DEFAULT_PART_SIZE) { const abortController = new AbortController(); + return createUploadTask({ isMultipartUpload: false, job: putObjectJob(input, abortController.signal, dataByteLength), @@ -79,6 +88,7 @@ export const uploadData = (input: UploadDataInput): UploadDataOutput => { } else { const { multipartUploadJob, onPause, onResume, onCancel } = getMultipartUploadHandlers(input, dataByteLength); + return createUploadTask({ isMultipartUpload: true, job: multipartUploadJob, diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/calculatePartSize.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/calculatePartSize.ts index d12379a0075..28ae4970d84 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/calculatePartSize.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/calculatePartSize.ts @@ -1,7 +1,10 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { DEFAULT_PART_SIZE, MAX_PARTS_COUNT } from '../../../utils/constants'; +import { + DEFAULT_PART_SIZE, + MAX_PARTS_COUNT, +} from '~/src/providers/s3/utils/constants'; export const calculatePartSize = (totalSize?: number): number => { if (!totalSize) { @@ -13,5 +16,6 @@ export const calculatePartSize = (totalSize?: number): number => { partSize *= 2; partsCount = Math.ceil(totalSize / partSize); } + return partSize; }; diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts index 4289152bb91..4dd3cd8f95e 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/getDataChunker.ts @@ -1,23 +1,24 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { StorageUploadDataPayload } from '../../../../../types'; +import { StorageUploadDataPayload } from '~/src/types'; import { StorageValidationErrorCode, validationErrorMap, -} from '../../../../../errors/types/validation'; -import { StorageError } from '../../../../../errors/StorageError'; +} from '~/src/errors/types/validation'; +import { StorageError } from '~/src/errors/StorageError'; + import { calculatePartSize } from './calculatePartSize'; -export type PartToUpload = { +export interface PartToUpload { partNumber: number; data: Blob | ArrayBuffer | string; size: number; -}; +} export const getDataChunker = ( data: StorageUploadDataPayload, - totalSize?: number + totalSize?: number, ): Generator => { const partSize = calculatePartSize(totalSize); @@ -29,6 +30,7 @@ export const getDataChunker = ( return helper(data, 0, data.byteLength, partSize); } else if (typeof data === 'string') { const blob = new Blob([data]); + return getDataChunker(blob, blob.size); } else { throw new StorageError({ @@ -42,7 +44,7 @@ const helper = function* ( buffer: ArrayBuffer | Blob, byteOffset: number, byteLength: number, - partSize: number + partSize: number, ): Generator { let partNumber = 1; let startByte = byteOffset; diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts index c7b8f64dfc6..4169fb5bba8 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/initialUpload.ts @@ -2,18 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { StorageAccessLevel } from '@aws-amplify/core'; +import { ResolvedS3Config } from '~/src/providers/s3/types/options'; +import { StorageUploadDataPayload } from '~/src/types'; +import { Part, createMultipartUpload } from '~/src/providers/s3/utils/client'; +import { logger } from '~/src/utils'; import { cacheMultipartUpload, findCachedUploadParts, getUploadsCacheKey, } from './uploadCache'; -import { ResolvedS3Config } from '../../../types/options'; -import { StorageUploadDataPayload } from '../../../../../types'; -import { Part, createMultipartUpload } from '../../../utils/client'; -import { logger } from '../../../../../utils'; -type LoadOrCreateMultipartUploadOptions = { +interface LoadOrCreateMultipartUploadOptions { s3Config: ResolvedS3Config; data: StorageUploadDataPayload; bucket: string; @@ -26,12 +26,12 @@ type LoadOrCreateMultipartUploadOptions = { metadata?: Record; size?: number; abortSignal?: AbortSignal; -}; +} -type LoadOrCreateMultipartUploadResult = { +interface LoadOrCreateMultipartUploadResult { uploadId: string; cachedParts: Part[]; -}; +} /** * Load the in-progress multipart upload from local storage or async storage(RN) if it exists, or create a new multipart @@ -103,10 +103,11 @@ export const loadOrCreateMultipartUpload = async ({ ContentDisposition: contentDisposition, ContentEncoding: contentEncoding, Metadata: metadata, - } + }, ); if (size === undefined) { logger.debug('uploaded data size cannot be determined, skipping cache.'); + return { uploadId: UploadId!, cachedParts: [], @@ -126,6 +127,7 @@ export const loadOrCreateMultipartUpload = async ({ key, fileName: data instanceof File ? data.name : '', }); + return { uploadId: UploadId!, cachedParts: [], diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/progressTracker.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/progressTracker.ts index 58e19c439b7..953d39e7907 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/progressTracker.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/progressTracker.ts @@ -1,12 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { TransferProgressEvent } from '../../../../../types'; +import { TransferProgressEvent } from '~/src/types'; -type ConcurrentUploadsProgressTrackerOptions = { +interface ConcurrentUploadsProgressTrackerOptions { size?: number; - onProgress?: (event: TransferProgressEvent) => void; -}; + onProgress?(event: TransferProgressEvent): void; +} /** * Track the progress from multiple concurrent uploads, and invoke the onProgress callback. @@ -22,13 +22,14 @@ export const getConcurrentUploadsProgressTracker = ({ const getTransferredBytes = () => transferredBytesPerListener.reduce( (acc, transferredBytes) => acc + transferredBytes, - 0 + 0, ); return { getOnProgressListener: () => { transferredBytesPerListener.push(0); const listenerIndex = transferredBytesPerListener.length - 1; + return (event: TransferProgressEvent) => { const { transferredBytes } = event; transferredBytesPerListener[listenerIndex] = transferredBytes; diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadCache.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadCache.ts index 251b5a1bf60..29159d7ae4a 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadCache.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadCache.ts @@ -2,24 +2,23 @@ // SPDX-License-Identifier: Apache-2.0 import { - defaultStorage, KeyValueStorageInterface, StorageAccessLevel, + defaultStorage, } from '@aws-amplify/core'; - -import { UPLOADS_STORAGE_KEY } from '../../../utils/constants'; -import { ResolvedS3Config } from '../../../types/options'; -import { Part, listParts } from '../../../utils/client'; -import { logger } from '../../../../../utils'; +import { UPLOADS_STORAGE_KEY } from '~/src/providers/s3/utils/constants'; +import { ResolvedS3Config } from '~/src/providers/s3/types/options'; +import { Part, listParts } from '~/src/providers/s3/utils/client'; +import { logger } from '~/src/utils'; const ONE_HOUR = 1000 * 60 * 60; -type FindCachedUploadPartsOptions = { +interface FindCachedUploadPartsOptions { cacheKey: string; s3Config: ResolvedS3Config; bucket: string; finalKey: string; -}; +} /** * Find the cached multipart upload id and get the parts that have been uploaded @@ -47,7 +46,7 @@ export const findCachedUploadParts = async ({ await defaultStorage.setItem( UPLOADS_STORAGE_KEY, - JSON.stringify(cachedUploads) + JSON.stringify(cachedUploads), ); try { @@ -56,6 +55,7 @@ export const findCachedUploadParts = async ({ Key: finalKey, UploadId: cachedUpload.uploadId, }); + return { parts: Parts, uploadId: cachedUpload.uploadId, @@ -63,38 +63,40 @@ export const findCachedUploadParts = async ({ } catch (e) { logger.debug('failed to list cached parts, removing cached upload.'); await removeCachedUpload(cacheKey); + return null; } }; -type FileMetadata = { +interface FileMetadata { bucket: string; fileName: string; key: string; uploadId: string; // Unix timestamp in ms lastTouched: number; -}; +} const listCachedUploadTasks = async ( - kvStorage: KeyValueStorageInterface + kvStorage: KeyValueStorageInterface, ): Promise> => { try { return JSON.parse((await kvStorage.getItem(UPLOADS_STORAGE_KEY)) ?? '{}'); } catch (e) { logger.debug('failed to parse cached uploads record.'); + return {}; } }; -type UploadsCacheKeyOptions = { +interface UploadsCacheKeyOptions { size: number; contentType?: string; bucket: string; accessLevel: StorageAccessLevel; key: string; file?: File; -}; +} /** * Get the cache key of a multipart upload. Data source cached by different: size, content type, bucket, access level, @@ -122,7 +124,7 @@ export const getUploadsCacheKey = ({ export const cacheMultipartUpload = async ( cacheKey: string, - fileMetadata: Omit + fileMetadata: Omit, ): Promise => { const cachedUploads = await listCachedUploadTasks(defaultStorage); cachedUploads[cacheKey] = { @@ -131,7 +133,7 @@ export const cacheMultipartUpload = async ( }; await defaultStorage.setItem( UPLOADS_STORAGE_KEY, - JSON.stringify(cachedUploads) + JSON.stringify(cachedUploads), ); }; @@ -140,6 +142,6 @@ export const removeCachedUpload = async (cacheKey: string): Promise => { delete cachedUploads[cacheKey]; await defaultStorage.setItem( UPLOADS_STORAGE_KEY, - JSON.stringify(cachedUploads) + JSON.stringify(cachedUploads), ); }; diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts index bd560fc0406..24e183b027a 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadHandlers.ts @@ -3,30 +3,32 @@ import { Amplify, StorageAccessLevel } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; - -import { getDataChunker } from './getDataChunker'; -import { UploadDataInput } from '../../../types'; -import { resolveS3ConfigAndInput } from '../../../utils'; -import { Item as S3Item } from '../../../types/outputs'; +import { UploadDataInput } from '~/src/providers/s3/types'; +import { + ResolvedS3ConfigAndInput, + resolveS3ConfigAndInput, +} from '~/src/providers/s3/utils/resolveS3ConfigAndInput'; +import { Item as S3Item } from '~/src/providers/s3/types/outputs'; import { DEFAULT_ACCESS_LEVEL, DEFAULT_QUEUE_SIZE, -} from '../../../utils/constants'; -import { loadOrCreateMultipartUpload } from './initialUpload'; -import { ResolvedS3Config } from '../../../types/options'; -import { getConcurrentUploadsProgressTracker } from './progressTracker'; -import { getUploadsCacheKey, removeCachedUpload } from './uploadCache'; -import { uploadPartExecutor } from './uploadPartExecutor'; -import { StorageError } from '../../../../../errors/StorageError'; -import { CanceledError } from '../../../../../errors/CanceledError'; +} from '~/src/providers/s3/utils/constants'; +import { StorageError } from '~/src/errors/StorageError'; +import { CanceledError } from '~/src/errors/CanceledError'; import { Part, abortMultipartUpload, completeMultipartUpload, headObject, -} from '../../../utils/client'; -import { getStorageUserAgentValue } from '../../../utils/userAgent'; -import { logger } from '../../../../../utils'; +} from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; +import { logger } from '~/src/utils'; + +import { uploadPartExecutor } from './uploadPartExecutor'; +import { getUploadsCacheKey, removeCachedUpload } from './uploadCache'; +import { getConcurrentUploadsProgressTracker } from './progressTracker'; +import { loadOrCreateMultipartUpload } from './initialUpload'; +import { getDataChunker } from './getDataChunker'; /** * Create closure hiding the multipart upload implementation details and expose the upload job and control functions( @@ -36,7 +38,7 @@ import { logger } from '../../../../../utils'; */ export const getMultipartUploadHandlers = ( { options: uploadDataOptions, key, data }: UploadDataInput, - size?: number + size?: number, ) => { let resolveCallback: ((value: S3Item) => void) | undefined; let rejectCallback: ((reason?: any) => void) | undefined; @@ -46,24 +48,21 @@ export const getMultipartUploadHandlers = ( completedParts: Part[]; } | undefined; - let s3Config: ResolvedS3Config | undefined; + let resolvedS3ConfigAndInput: ResolvedS3ConfigAndInput; let abortController: AbortController | undefined; - let bucket: string | undefined; - let keyPrefix: string | undefined; let uploadCacheKey: string | undefined; // Special flag that differentiates HTTP requests abort error caused by pause() from ones caused by cancel(). // The former one should NOT cause the upload job to throw, but cancels any pending HTTP requests. // This should be replaced by a special abort reason. However,the support of this API is lagged behind. - let isAbortSignalFromPause: boolean = false; + let isAbortSignalFromPause = false; const startUpload = async (): Promise => { - const resolvedS3Options = await resolveS3ConfigAndInput( + resolvedS3ConfigAndInput = await resolveS3ConfigAndInput( Amplify, - uploadDataOptions + uploadDataOptions, ); - s3Config = resolvedS3Options.s3Config; - bucket = resolvedS3Options.bucket; - keyPrefix = resolvedS3Options.keyPrefix; + + const { s3Config, bucket, keyPrefix } = resolvedS3ConfigAndInput; abortController = new AbortController(); isAbortSignalFromPause = false; @@ -112,7 +111,7 @@ export const getMultipartUploadHandlers = ( const dataChunker = getDataChunker(data, size); const completedPartNumberSet = new Set( - inProgressUpload.completedParts.map(({ PartNumber }) => PartNumber!) + inProgressUpload.completedParts.map(({ PartNumber }) => PartNumber!), ); const onPartUploadCompletion = (partNumber: number, eTag: string) => { inProgressUpload?.completedParts.push({ @@ -139,8 +138,8 @@ export const getMultipartUploadHandlers = ( uploadId: inProgressUpload.uploadId, onPartUploadCompletion, onProgress: concurrentUploadsProgressTracker.getOnProgressListener(), - isObjectLockEnabled: resolvedS3Options.isObjectLockEnabled, - }) + isObjectLockEnabled: resolvedS3ConfigAndInput.isObjectLockEnabled, + }), ); } @@ -158,10 +157,10 @@ export const getMultipartUploadHandlers = ( UploadId: inProgressUpload.uploadId, MultipartUpload: { Parts: inProgressUpload.completedParts.sort( - (partA, partB) => partA.PartNumber! - partB.PartNumber! + (partA, partB) => partA.PartNumber! - partB.PartNumber!, ), }, - } + }, ); if (size) { @@ -225,6 +224,9 @@ export const getMultipartUploadHandlers = ( await removeCachedUpload(uploadCacheKey); } // 3. clear multipart upload on server side. + // resolvedS3ConfigAndInput has been assigned within `startUpload` + // which ensures it's available at this point. + const { s3Config, bucket, keyPrefix } = resolvedS3ConfigAndInput!; await abortMultipartUpload(s3Config!, { Bucket: bucket, Key: keyPrefix! + key, @@ -238,9 +240,10 @@ export const getMultipartUploadHandlers = ( rejectCallback!( // Internal error that should not be exposed to the users. They should use isCancelError() to check if // the error is caused by cancel(). - new CanceledError(message ? { message } : undefined) + new CanceledError(message ? { message } : undefined), ); }; + return { multipartUploadJob, onPause, diff --git a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadPartExecutor.ts b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadPartExecutor.ts index b8c2b7f43c1..c7cf1743423 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadPartExecutor.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/multipart/uploadPartExecutor.ts @@ -1,14 +1,15 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { TransferProgressEvent } from '~/src/types'; +import { ResolvedS3Config } from '~/src/providers/s3/types/options'; +import { calculateContentMd5 } from '~/src/providers/s3/utils'; +import { uploadPart } from '~/src/providers/s3/utils/client'; +import { logger } from '~/src/utils'; + import { PartToUpload } from './getDataChunker'; -import { TransferProgressEvent } from '../../../../../types'; -import { ResolvedS3Config } from '../../../types/options'; -import { calculateContentMd5 } from '../../../utils'; -import { uploadPart } from '../../../utils/client'; -import { logger } from '../../../../../utils'; -type UploadPartExecutorOptions = { +interface UploadPartExecutorOptions { dataChunkerGenerator: Generator; completedPartNumberSet: Set; s3Config: ResolvedS3Config; @@ -17,9 +18,9 @@ type UploadPartExecutorOptions = { finalKey: string; uploadId: string; isObjectLockEnabled?: boolean; - onPartUploadCompletion: (partNumber: number, eTag: string) => void; - onProgress?: (event: TransferProgressEvent) => void; -}; + onPartUploadCompletion(partNumber: number, eTag: string): void; + onProgress?(event: TransferProgressEvent): void; +} export const uploadPartExecutor = async ({ dataChunkerGenerator, @@ -68,7 +69,7 @@ export const uploadPartExecutor = async ({ ContentMD5: isObjectLockEnabled ? await calculateContentMd5(data) : undefined, - } + }, ); transferredBytes += size; // eTag will always be set even the S3 model interface marks it as optional. diff --git a/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts b/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts index 0842197acf8..d70806a36af 100644 --- a/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts +++ b/packages/storage/src/providers/s3/apis/uploadData/putObjectJob.ts @@ -3,12 +3,14 @@ import { Amplify } from '@aws-amplify/core'; import { StorageAction } from '@aws-amplify/core/internals/utils'; - -import { UploadDataInput } from '../../types'; -import { calculateContentMd5, resolveS3ConfigAndInput } from '../../utils'; -import { Item as S3Item } from '../../types/outputs'; -import { putObject } from '../../utils/client'; -import { getStorageUserAgentValue } from '../../utils/userAgent'; +import { UploadDataInput } from '~/src/providers/s3/types'; +import { + calculateContentMd5, + resolveS3ConfigAndInput, +} from '~/src/providers/s3/utils'; +import { Item as S3Item } from '~/src/providers/s3/types/outputs'; +import { putObject } from '~/src/providers/s3/utils/client'; +import { getStorageUserAgentValue } from '~/src/providers/s3/utils/userAgent'; /** * Get a function the returns a promise to call putObject API to S3. @@ -19,7 +21,7 @@ export const putObjectJob = ( { options: uploadDataOptions, key, data }: UploadDataInput, abortSignal: AbortSignal, - totalLength?: number + totalLength?: number, ) => async (): Promise => { const { bucket, keyPrefix, s3Config, isObjectLockEnabled } = @@ -52,7 +54,7 @@ export const putObjectJob = ContentMD5: isObjectLockEnabled ? await calculateContentMd5(data) : undefined, - } + }, ); return { diff --git a/packages/storage/src/providers/s3/types/inputs.ts b/packages/storage/src/providers/s3/types/inputs.ts index 51c25ab4b3f..15cea3af98b 100644 --- a/packages/storage/src/providers/s3/types/inputs.ts +++ b/packages/storage/src/providers/s3/types/inputs.ts @@ -3,24 +3,24 @@ import { StorageCopyInput, + StorageDownloadDataInput, StorageGetPropertiesInput, StorageGetUrlInput, StorageListInput, StorageRemoveInput, - StorageDownloadDataInput, StorageUploadDataInput, -} from '../../../types'; +} from '~/src/types'; import { + CopyDestinationOptions, + CopySourceOptions, + DownloadDataOptions, GetPropertiesOptions, GetUrlOptions, ListAllOptions, ListPaginateOptions, RemoveOptions, - DownloadDataOptions, UploadDataOptions, - CopyDestinationOptions, - CopySourceOptions, -} from '../types'; +} from '~/src/providers/s3/types'; // TODO: support use accelerate endpoint option /** diff --git a/packages/storage/src/providers/s3/types/options.ts b/packages/storage/src/providers/s3/types/options.ts index dace141e6b9..c3268ffb7ba 100644 --- a/packages/storage/src/providers/s3/types/options.ts +++ b/packages/storage/src/providers/s3/types/options.ts @@ -3,45 +3,44 @@ import { StorageAccessLevel } from '@aws-amplify/core'; import { AWSCredentials } from '@aws-amplify/core/internals/utils'; - -import { TransferProgressEvent } from '../../../types'; +import { TransferProgressEvent } from '~/src/types'; import { StorageListAllOptions, StorageListPaginateOptions, -} from '../../../types/options'; +} from '~/src/types/options'; -type CommonOptions = { +interface CommonOptions { /** * Whether to use accelerate endpoint. * @default false */ useAccelerateEndpoint?: boolean; -}; +} type ReadOptions = | { accessLevel?: 'guest' | 'private' } | { accessLevel: 'protected'; targetIdentityId?: string }; -type WriteOptions = { +interface WriteOptions { accessLevel?: StorageAccessLevel; -}; +} -type BytesRangeOptions = { +interface BytesRangeOptions { bytesRange?: { start: number; end: number; }; -}; +} /** * Transfer-related options type for S3 downloadData, uploadData APIs. */ -type TransferOptions = { +interface TransferOptions { /** * Callback function tracking the upload/download progress. */ - onProgress?: (event: TransferProgressEvent) => void; -}; + onProgress?(event: TransferProgressEvent): void; +} /** * Input options type for S3 getProperties API. @@ -130,10 +129,10 @@ export type CopyDestinationOptions = WriteOptions & { * * @internal */ -export type ResolvedS3Config = { +export interface ResolvedS3Config { region: string; credentials: AWSCredentials; customEndpoint?: string; forcePathStyle?: boolean; useAccelerateEndpoint?: boolean; -}; +} diff --git a/packages/storage/src/providers/s3/types/outputs.ts b/packages/storage/src/providers/s3/types/outputs.ts index 532a4a455f1..97fe62f328c 100644 --- a/packages/storage/src/providers/s3/types/outputs.ts +++ b/packages/storage/src/providers/s3/types/outputs.ts @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { + DownloadTask, StorageDownloadDataOutput, StorageGetUrlOutput, StorageItem, StorageListOutput, - DownloadTask, UploadTask, -} from '../../../types'; +} from '~/src/types'; /** * type for S3 item. diff --git a/packages/storage/src/providers/s3/utils/client/abortMultipartUpload.ts b/packages/storage/src/providers/s3/utils/client/abortMultipartUpload.ts index 301092613ac..59a3939b765 100644 --- a/packages/storage/src/providers/s3/utils/client/abortMultipartUpload.ts +++ b/packages/storage/src/providers/s3/utils/client/abortMultipartUpload.ts @@ -13,7 +13,7 @@ import { AmplifyUrlSearchParams, } from '@aws-amplify/core/internals/utils'; import { MetadataBearer } from '@aws-sdk/types'; -import type { AbortMultipartUploadCommandInput } from './types'; + import { defaultConfig } from './base'; import { @@ -24,6 +24,8 @@ import { validateS3RequiredParameter, } from './utils'; +import type { AbortMultipartUploadCommandInput } from './types'; + export type AbortMultipartUploadInput = Pick< AbortMultipartUploadCommandInput, 'Bucket' | 'Key' | 'UploadId' @@ -33,7 +35,7 @@ export type AbortMultipartUploadOutput = MetadataBearer; const abortMultipartUploadSerializer = ( input: AbortMultipartUploadInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); @@ -42,6 +44,7 @@ const abortMultipartUploadSerializer = ( url.search = new AmplifyUrlSearchParams({ uploadId: input.UploadId, }).toString(); + return { method: 'DELETE', headers: {}, @@ -50,7 +53,7 @@ const abortMultipartUploadSerializer = ( }; const abortMultipartUploadDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -66,5 +69,5 @@ export const abortMultipartUpload = composeServiceApi( s3TransferHandler, abortMultipartUploadSerializer, abortMultipartUploadDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/base.ts b/packages/storage/src/providers/s3/utils/client/base.ts index 5638402d351..96f0e5958ef 100644 --- a/packages/storage/src/providers/s3/utils/client/base.ts +++ b/packages/storage/src/providers/s3/utils/client/base.ts @@ -2,18 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 import { - getAmplifyUserAgent, AmplifyUrl, + getAmplifyUserAgent, } from '@aws-amplify/core/internals/utils'; import { + EndpointResolverOptions, getDnsSuffix, - jitteredBackoff, getRetryDecider, - EndpointResolverOptions, + jitteredBackoff, } from '@aws-amplify/core/internals/aws-client-utils'; + import { parseXmlError } from './utils'; -const DOMAIN_PATTERN = /^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$/; +const DOMAIN_PATTERN = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/; const IP_ADDRESS_PATTERN = /(\d+\.){3}\d+/; const DOTS_PATTERN = /\.\./; @@ -52,7 +53,7 @@ export type S3EndpointResolverOptions = EndpointResolverOptions & { */ const endpointResolver = ( options: S3EndpointResolverOptions, - apiInput?: { Bucket?: string } + apiInput?: { Bucket?: string }, ) => { const { region, useAccelerateEndpoint, customEndpoint, forcePathStyle } = options; @@ -63,7 +64,7 @@ const endpointResolver = ( } else if (useAccelerateEndpoint) { if (forcePathStyle) { throw new Error( - 'Path style URLs are not supported with S3 Transfer Acceleration.' + 'Path style URLs are not supported with S3 Transfer Acceleration.', ); } endpoint = new AmplifyUrl(`https://s3-accelerate.${getDnsSuffix(region)}`); @@ -81,6 +82,7 @@ const endpointResolver = ( endpoint.host = `${apiInput.Bucket}.${endpoint.host}`; } } + return { url: endpoint }; }; diff --git a/packages/storage/src/providers/s3/utils/client/completeMultipartUpload.ts b/packages/storage/src/providers/s3/utils/client/completeMultipartUpload.ts index 55e52fbcb7d..023e185849a 100644 --- a/packages/storage/src/providers/s3/utils/client/completeMultipartUpload.ts +++ b/packages/storage/src/providers/s3/utils/client/completeMultipartUpload.ts @@ -12,12 +12,7 @@ import { AmplifyUrlSearchParams, } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import type { - CompleteMultipartUploadCommandInput, - CompleteMultipartUploadCommandOutput, - CompletedMultipartUpload, - CompletedPart, -} from './types'; + import { defaultConfig } from './base'; import { buildStorageServiceError, @@ -29,6 +24,13 @@ import { validateS3RequiredParameter, } from './utils'; +import type { + CompleteMultipartUploadCommandInput, + CompleteMultipartUploadCommandOutput, + CompletedMultipartUpload, + CompletedPart, +} from './types'; + const INVALID_PARAMETER_ERROR_MSG = 'Invalid parameter for ComplteMultipartUpload API'; @@ -44,7 +46,7 @@ export type CompleteMultipartUploadOutput = Pick< const completeMultipartUploadSerializer = async ( input: CompleteMultipartUploadInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const headers = { 'content-type': 'application/xml', @@ -57,6 +59,7 @@ const completeMultipartUploadSerializer = async ( uploadId: input.UploadId, }).toString(); validateS3RequiredParameter(!!input.MultipartUpload, 'MultipartUpload'); + return { method: 'POST', headers, @@ -68,13 +71,14 @@ const completeMultipartUploadSerializer = async ( }; const serializeCompletedMultipartUpload = ( - input: CompletedMultipartUpload + input: CompletedMultipartUpload, ): string => { if (!input.Parts?.length) { throw new Error(`${INVALID_PARAMETER_ERROR_MSG}: ${input}`); } + return `${input.Parts.map( - serializeCompletedPartList + serializeCompletedPartList, ).join('')}`; }; @@ -82,6 +86,7 @@ const serializeCompletedPartList = (input: CompletedPart): string => { if (!input.ETag || input.PartNumber == null) { throw new Error(`${INVALID_PARAMETER_ERROR_MSG}: ${input}`); } + return `${input.ETag}${input.PartNumber}`; }; @@ -100,11 +105,12 @@ const parseXmlBodyOrThrow = async (response: HttpResponse): Promise => { })) as Error; throw buildStorageServiceError(error, response.statusCode); } + return parsed; }; const completeMultipartUploadDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -116,6 +122,7 @@ const completeMultipartUploadDeserializer = async ( Key: 'Key', Location: 'Location', }); + return { $metadata: parseMetadata(response), ...contents, @@ -128,7 +135,7 @@ const completeMultipartUploadDeserializer = async ( // Ref: https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html#API_CompleteMultipartUpload_Example_4 const retryWhenErrorWith200StatusCode = async ( response?: HttpResponse, - error?: unknown + error?: unknown, ): Promise => { if (!response) { return false; @@ -141,10 +148,12 @@ const retryWhenErrorWith200StatusCode = async ( if (parsed.Code !== undefined && parsed.Message !== undefined) { return true; } + return false; } const defaultRetryDecider = defaultConfig.retryDecider; + return defaultRetryDecider(response, error); }; @@ -156,5 +165,5 @@ export const completeMultipartUpload = composeServiceApi( ...defaultConfig, responseType: 'text', retryDecider: retryWhenErrorWith200StatusCode, - } + }, ); diff --git a/packages/storage/src/providers/s3/utils/client/copyObject.ts b/packages/storage/src/providers/s3/utils/client/copyObject.ts index 6a79bfb6d9f..508ea9fe65b 100644 --- a/packages/storage/src/providers/s3/utils/client/copyObject.ts +++ b/packages/storage/src/providers/s3/utils/client/copyObject.ts @@ -9,19 +9,21 @@ import { } from '@aws-amplify/core/internals/aws-client-utils'; import { AmplifyUrl } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import type { CopyObjectCommandInput, CopyObjectCommandOutput } from './types'; + import { defaultConfig } from './base'; import { + assignStringVariables, buildStorageServiceError, parseXmlBody, parseXmlError, s3TransferHandler, - assignStringVariables, serializeObjectConfigsToHeaders, serializePathnameObjectKey, validateS3RequiredParameter, } from './utils'; +import type { CopyObjectCommandInput, CopyObjectCommandOutput } from './types'; + export type CopyObjectInput = Pick< CopyObjectCommandInput, | 'Bucket' @@ -42,7 +44,7 @@ export type CopyObjectOutput = CopyObjectCommandOutput; const copyObjectSerializer = async ( input: CopyObjectInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const headers = { ...(await serializeObjectConfigsToHeaders(input)), @@ -54,6 +56,7 @@ const copyObjectSerializer = async ( const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); url.pathname = serializePathnameObjectKey(url, input.Key); + return { method: 'PUT', headers, @@ -62,13 +65,14 @@ const copyObjectSerializer = async ( }; const copyObjectDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; throw buildStorageServiceError(error, response.statusCode); } else { await parseXmlBody(response); + return { $metadata: parseMetadata(response), }; @@ -79,5 +83,5 @@ export const copyObject = composeServiceApi( s3TransferHandler, copyObjectSerializer, copyObjectDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/createMultipartUpload.ts b/packages/storage/src/providers/s3/utils/client/createMultipartUpload.ts index 62c72d6e94c..e33db6f52b1 100644 --- a/packages/storage/src/providers/s3/utils/client/createMultipartUpload.ts +++ b/packages/storage/src/providers/s3/utils/client/createMultipartUpload.ts @@ -9,24 +9,26 @@ import { } from '@aws-amplify/core/internals/aws-client-utils'; import { AmplifyUrl } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import type { - CreateMultipartUploadCommandInput, - CreateMultipartUploadCommandOutput, -} from './types'; -import type { PutObjectInput } from './putObject'; import { defaultConfig } from './base'; import { buildStorageServiceError, - validateS3RequiredParameter, map, parseXmlBody, parseXmlError, s3TransferHandler, serializeObjectConfigsToHeaders, serializePathnameObjectKey, + validateS3RequiredParameter, } from './utils'; +import type { + CreateMultipartUploadCommandInput, + CreateMultipartUploadCommandOutput, +} from './types'; +import type { PutObjectInput } from './putObject'; + + export type CreateMultipartUploadInput = Extract< CreateMultipartUploadCommandInput, PutObjectInput @@ -39,13 +41,14 @@ export type CreateMultipartUploadOutput = Pick< const createMultipartUploadSerializer = async ( input: CreateMultipartUploadInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const headers = await serializeObjectConfigsToHeaders(input); const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); url.pathname = serializePathnameObjectKey(url, input.Key); url.search = 'uploads'; + return { method: 'POST', headers, @@ -54,7 +57,7 @@ const createMultipartUploadSerializer = async ( }; const createMultipartUploadDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -64,6 +67,7 @@ const createMultipartUploadDeserializer = async ( const contents = map(parsed, { UploadId: 'UploadId', }); + return { $metadata: parseMetadata(response), ...contents, @@ -75,5 +79,5 @@ export const createMultipartUpload = composeServiceApi( s3TransferHandler, createMultipartUploadSerializer, createMultipartUploadDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/deleteObject.ts b/packages/storage/src/providers/s3/utils/client/deleteObject.ts index 8ba2ea864d7..95ede3fa63f 100644 --- a/packages/storage/src/providers/s3/utils/client/deleteObject.ts +++ b/packages/storage/src/providers/s3/utils/client/deleteObject.ts @@ -9,10 +9,6 @@ import { } from '@aws-amplify/core/internals/aws-client-utils'; import { AmplifyUrl } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import type { - DeleteObjectCommandInput, - DeleteObjectCommandOutput, -} from './types'; import { defaultConfig } from './base'; import { @@ -24,7 +20,13 @@ import { serializePathnameObjectKey, validateS3RequiredParameter, } from './utils'; -import { StorageError } from '../../../../errors/StorageError'; + +import type { + DeleteObjectCommandInput, + DeleteObjectCommandOutput, +} from './types'; + + export type DeleteObjectInput = Pick< DeleteObjectCommandInput, @@ -35,11 +37,12 @@ export type DeleteObjectOutput = DeleteObjectCommandOutput; const deleteObjectSerializer = ( input: DeleteObjectInput, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); url.pathname = serializePathnameObjectKey(url, input.Key); + return { method: 'DELETE', headers: {}, @@ -48,7 +51,7 @@ const deleteObjectSerializer = ( }; const deleteObjectDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { // error is always set when statusCode >= 300 @@ -60,6 +63,7 @@ const deleteObjectDeserializer = async ( VersionId: 'x-amz-version-id', RequestCharged: 'x-amz-request-charged', }); + return { ...content, $metadata: parseMetadata(response), @@ -71,5 +75,5 @@ export const deleteObject = composeServiceApi( s3TransferHandler, deleteObjectSerializer, deleteObjectDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/getObject.ts b/packages/storage/src/providers/s3/utils/client/getObject.ts index 17278aaa67e..13b45b591ae 100644 --- a/packages/storage/src/providers/s3/utils/client/getObject.ts +++ b/packages/storage/src/providers/s3/utils/client/getObject.ts @@ -2,25 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 import { + EMPTY_SHA256_HASH, Endpoint, HttpRequest, + HttpResponse, + PresignUrlOptions, + UserAgentOptions, parseMetadata, presignUrl, - UserAgentOptions, - PresignUrlOptions, - EMPTY_SHA256_HASH, - HttpResponse, } from '@aws-amplify/core/internals/aws-client-utils'; import { AmplifyUrl } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; import { S3EndpointResolverOptions, defaultConfig } from './base'; -import type { - CompatibleHttpResponse, - GetObjectCommandInput, - GetObjectCommandOutput, -} from './types'; import { + CONTENT_SHA256_HEADER, buildStorageServiceError, deserializeBoolean, deserializeMetadata, @@ -30,10 +26,15 @@ import { parseXmlError, s3TransferHandler, serializePathnameObjectKey, - CONTENT_SHA256_HEADER, validateS3RequiredParameter, } from './utils'; +import type { + CompatibleHttpResponse, + GetObjectCommandInput, + GetObjectCommandOutput, +} from './types'; + const USER_AGENT_HEADER = 'x-amz-user-agent'; export type GetObjectInput = Pick< @@ -45,11 +46,12 @@ export type GetObjectOutput = GetObjectCommandOutput; const getObjectSerializer = async ( input: GetObjectInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); url.pathname = serializePathnameObjectKey(url, input.Key); + return { method: 'GET', headers: { @@ -60,7 +62,7 @@ const getObjectSerializer = async ( }; const getObjectDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -122,7 +124,7 @@ export const getObject = composeServiceApi( s3TransferHandler, getObjectSerializer, getObjectDeserializer, - { ...defaultConfig, responseType: 'blob' } + { ...defaultConfig, responseType: 'blob' }, ); type S3GetObjectPresignedUrlConfig = Omit< @@ -140,7 +142,7 @@ type S3GetObjectPresignedUrlConfig = Omit< */ export const getPresignedGetObjectUrl = async ( config: S3GetObjectPresignedUrlConfig, - input: GetObjectInput + input: GetObjectInput, ): Promise => { const endpoint = defaultConfig.endpointResolver(config, input); const { url, headers, method } = await getObjectSerializer(input, endpoint); @@ -152,15 +154,16 @@ export const getPresignedGetObjectUrl = async ( if (config.userAgentValue) { url.searchParams.append( config.userAgentHeader ?? USER_AGENT_HEADER, - config.userAgentValue + config.userAgentValue, ); } for (const [headerName, value] of Object.entries(headers).sort( - ([key1], [key2]) => key1.localeCompare(key2) + ([key1], [key2]) => key1.localeCompare(key2), )) { url.searchParams.append(headerName, value); } + return presignUrl( { method, url, body: undefined }, { @@ -168,6 +171,6 @@ export const getPresignedGetObjectUrl = async ( signingRegion: config.region, ...defaultConfig, ...config, - } + }, ); }; diff --git a/packages/storage/src/providers/s3/utils/client/headObject.ts b/packages/storage/src/providers/s3/utils/client/headObject.ts index 580162edff7..91b356d24df 100644 --- a/packages/storage/src/providers/s3/utils/client/headObject.ts +++ b/packages/storage/src/providers/s3/utils/client/headObject.ts @@ -9,9 +9,8 @@ import { } from '@aws-amplify/core/internals/aws-client-utils'; import { AmplifyUrl } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import { defaultConfig } from './base'; -import type { HeadObjectCommandInput, HeadObjectCommandOutput } from './types'; +import { defaultConfig } from './base'; import { buildStorageServiceError, deserializeMetadata, @@ -23,7 +22,9 @@ import { serializePathnameObjectKey, validateS3RequiredParameter, } from './utils'; -import { StorageError } from '../../../../errors/StorageError'; + +import type { HeadObjectCommandInput, HeadObjectCommandOutput } from './types'; + export type HeadObjectInput = Pick; @@ -40,11 +41,12 @@ export type HeadObjectOutput = Pick< const headObjectSerializer = async ( input: HeadObjectInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); url.pathname = serializePathnameObjectKey(url, input.Key); + return { method: 'HEAD', headers: {}, @@ -53,7 +55,7 @@ const headObjectSerializer = async ( }; const headObjectDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { // error is always set when statusCode >= 300 @@ -70,6 +72,7 @@ const headObjectDeserializer = async ( }), Metadata: deserializeMetadata(response.headers), }; + return { $metadata: parseMetadata(response), ...contents, @@ -81,5 +84,5 @@ export const headObject = composeServiceApi( s3TransferHandler, headObjectSerializer, headObjectDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/listObjectsV2.ts b/packages/storage/src/providers/s3/utils/client/listObjectsV2.ts index ab2e5d2ebf6..2f657360b10 100644 --- a/packages/storage/src/providers/s3/utils/client/listObjectsV2.ts +++ b/packages/storage/src/providers/s3/utils/client/listObjectsV2.ts @@ -12,10 +12,7 @@ import { AmplifyUrlSearchParams, } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import type { - ListObjectsV2CommandInput, - ListObjectsV2CommandOutput, -} from './types'; + import { defaultConfig } from './base'; import { assignStringVariables, @@ -30,13 +27,18 @@ import { s3TransferHandler, } from './utils'; +import type { + ListObjectsV2CommandInput, + ListObjectsV2CommandOutput, +} from './types'; + export type ListObjectsV2Input = ListObjectsV2CommandInput; export type ListObjectsV2Output = ListObjectsV2CommandOutput; const listObjectsV2Serializer = ( input: ListObjectsV2Input, - endpoint: Endpoint + endpoint: Endpoint, ): HttpRequest => { const headers = assignStringVariables({ 'x-amz-request-payer': input.RequestPayer, @@ -54,6 +56,7 @@ const listObjectsV2Serializer = ( }); const url = new AmplifyUrl(endpoint.url.toString()); url.search = new AmplifyUrlSearchParams(query).toString(); + return { method: 'GET', headers, @@ -62,7 +65,7 @@ const listObjectsV2Serializer = ( }; const listObjectsV2Deserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { // error is always set when statusCode >= 300 @@ -90,6 +93,7 @@ const listObjectsV2Deserializer = async ( Prefix: 'Prefix', StartAfter: 'StartAfter', }); + return { $metadata: parseMetadata(response), ...contents, @@ -131,5 +135,5 @@ export const listObjectsV2 = composeServiceApi( s3TransferHandler, listObjectsV2Serializer, listObjectsV2Deserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/listParts.ts b/packages/storage/src/providers/s3/utils/client/listParts.ts index ca369f8f0c6..ed5c62cab01 100644 --- a/packages/storage/src/providers/s3/utils/client/listParts.ts +++ b/packages/storage/src/providers/s3/utils/client/listParts.ts @@ -12,24 +12,26 @@ import { AmplifyUrlSearchParams, } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import type { - ListPartsCommandInput, - ListPartsCommandOutput, - CompletedPart, -} from './types'; + import { defaultConfig } from './base'; import { buildStorageServiceError, + deserializeNumber, emptyArrayGuard, map, parseXmlBody, parseXmlError, s3TransferHandler, - deserializeNumber, serializePathnameObjectKey, validateS3RequiredParameter, } from './utils'; +import type { + CompletedPart, + ListPartsCommandInput, + ListPartsCommandOutput, +} from './types'; + export type ListPartsInput = Pick< ListPartsCommandInput, 'Bucket' | 'Key' | 'UploadId' @@ -42,7 +44,7 @@ export type ListPartsOutput = Pick< const listPartsSerializer = async ( input: ListPartsInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const headers = {}; const url = new AmplifyUrl(endpoint.url.toString()); @@ -52,6 +54,7 @@ const listPartsSerializer = async ( url.search = new AmplifyUrlSearchParams({ uploadId: input.UploadId, }).toString(); + return { method: 'GET', headers, @@ -60,7 +63,7 @@ const listPartsSerializer = async ( }; const listPartsDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -74,6 +77,7 @@ const listPartsDeserializer = async ( value => emptyArrayGuard(value, deserializeCompletedPartList), ], }); + return { $metadata: parseMetadata(response), ...contents, @@ -87,12 +91,12 @@ const deserializeCompletedPartList = (input: any[]): CompletedPart[] => PartNumber: ['PartNumber', deserializeNumber], ETag: 'ETag', Size: ['Size', deserializeNumber], - }) + }), ); export const listParts = composeServiceApi( s3TransferHandler, listPartsSerializer, listPartsDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/putObject.ts b/packages/storage/src/providers/s3/utils/client/putObject.ts index f7d5a1b6218..c7a9872bb6d 100644 --- a/packages/storage/src/providers/s3/utils/client/putObject.ts +++ b/packages/storage/src/providers/s3/utils/client/putObject.ts @@ -11,18 +11,19 @@ import { AmplifyUrl } from '@aws-amplify/core/internals/utils'; import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; import { defaultConfig } from './base'; -import type { PutObjectCommandInput, PutObjectCommandOutput } from './types'; import { - buildStorageServiceError, - validateS3RequiredParameter, assignStringVariables, + buildStorageServiceError, map, parseXmlError, s3TransferHandler, serializeObjectConfigsToHeaders, serializePathnameObjectKey, + validateS3RequiredParameter, } from './utils'; +import type { PutObjectCommandInput, PutObjectCommandOutput } from './types'; + export type PutObjectInput = Pick< PutObjectCommandInput, | 'Bucket' @@ -48,7 +49,7 @@ export type PutObjectOutput = Pick< const putObjectSerializer = async ( input: PutObjectInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const headers = { ...(await serializeObjectConfigsToHeaders({ @@ -60,6 +61,7 @@ const putObjectSerializer = async ( const url = new AmplifyUrl(endpoint.url.toString()); validateS3RequiredParameter(!!input.Key, 'Key'); url.pathname = serializePathnameObjectKey(url, input.Key); + return { method: 'PUT', headers, @@ -69,7 +71,7 @@ const putObjectSerializer = async ( }; const putObjectDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -89,5 +91,5 @@ export const putObject = composeServiceApi( s3TransferHandler, putObjectSerializer, putObjectDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/runtime/base64/index.browser.ts b/packages/storage/src/providers/s3/utils/client/runtime/base64/index.browser.ts index 8190395051e..8ef5f37c850 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/base64/index.browser.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/base64/index.browser.ts @@ -3,6 +3,7 @@ function bytesToBase64(bytes: Uint8Array): string { const base64Str = Array.from(bytes, x => String.fromCodePoint(x)).join(''); + return btoa(base64Str); } @@ -16,6 +17,6 @@ export function toBase64(input: string | ArrayBufferView): string { } return bytesToBase64( - new Uint8Array(input.buffer, input.byteOffset, input.byteLength) + new Uint8Array(input.buffer, input.byteOffset, input.byteLength), ); } diff --git a/packages/storage/src/providers/s3/utils/client/runtime/base64/index.native.ts b/packages/storage/src/providers/s3/utils/client/runtime/base64/index.native.ts index eb1d809c252..1135ea400bd 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/base64/index.native.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/base64/index.native.ts @@ -12,5 +12,5 @@ export function toBase64(input: string | ArrayBufferView): string { return Buffer.from(input, 'utf-8').toString('base64'); } - return new Buffer(input.buffer).toString('base64'); + return Buffer.from(input.buffer).toString('base64'); } diff --git a/packages/storage/src/providers/s3/utils/client/runtime/contentSha256middleware.ts b/packages/storage/src/providers/s3/utils/client/runtime/contentSha256middleware.ts index 93d82446080..c7a9ba3d57f 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/contentSha256middleware.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/contentSha256middleware.ts @@ -3,10 +3,11 @@ import { HttpRequest, - getHashedPayload, - MiddlewareHandler, HttpResponse, + MiddlewareHandler, + getHashedPayload, } from '@aws-amplify/core/internals/aws-client-utils'; + import { CONTENT_SHA256_HEADER } from './constants'; /** @@ -17,13 +18,15 @@ import { CONTENT_SHA256_HEADER } from './constants'; * @internal */ export const contentSha256Middleware = - (options: {}) => (next: MiddlewareHandler) => - async function contentSha256Middleware(request: HttpRequest) { - if (request.headers[CONTENT_SHA256_HEADER]) { - return next(request); - } else { - const hash = await getHashedPayload(request.body); - request.headers[CONTENT_SHA256_HEADER] = hash; - return next(request); - } - }; + () => + (next: MiddlewareHandler) => + async (request: HttpRequest) => { + if (request.headers[CONTENT_SHA256_HEADER]) { + return next(request); + } else { + const hash = await getHashedPayload(request.body); + request.headers[CONTENT_SHA256_HEADER] = hash; + + return next(request); + } + }; diff --git a/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/fetch.ts b/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/fetch.ts index a805e6fbce1..6f8c5b79154 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/fetch.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/fetch.ts @@ -6,9 +6,10 @@ import { HttpResponse, authenticatedHandler, } from '@aws-amplify/core/internals/aws-client-utils'; -import type { s3TransferHandler as s3BrowserTransferhandler } from './xhr'; import { composeTransferHandler } from '@aws-amplify/core/internals/aws-client-utils/composers'; -import { contentSha256Middleware } from '../contentSha256middleware'; +import { contentSha256Middleware } from '~/src/providers/s3/utils/client/runtime/contentSha256middleware'; + +import type { s3TransferHandler as s3BrowserTransferHandler } from './xhr'; /** * S3 transfer handler for node based on Node-fetch. On top of basic transfer handler, it also supports @@ -16,9 +17,9 @@ import { contentSha256Middleware } from '../contentSha256middleware'; * * @internal */ -export const s3TransferHandler: typeof s3BrowserTransferhandler = +export const s3TransferHandler: typeof s3BrowserTransferHandler = composeTransferHandler< - [{}], + [object], HttpRequest, HttpResponse, typeof authenticatedHandler diff --git a/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/xhr.ts b/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/xhr.ts index 9be36001c6a..8e691d0e208 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/xhr.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/s3TransferHandler/xhr.ts @@ -2,19 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 import { - retryMiddleware, + HttpRequest, + HttpResponse, RetryOptions, - signingMiddleware, SigningOptions, - userAgentMiddleware, UserAgentOptions, - HttpRequest, - HttpResponse, + retryMiddleware, + signingMiddleware, + userAgentMiddleware, } from '@aws-amplify/core/internals/aws-client-utils'; import { composeTransferHandler } from '@aws-amplify/core/internals/aws-client-utils/composers'; - -import { contentSha256Middleware } from '../contentSha256middleware'; -import { xhrTransferHandler } from '../xhrTransferHandler'; +import { contentSha256Middleware } from '~/src/providers/s3/utils/client/runtime/contentSha256middleware'; +import { xhrTransferHandler } from '~/src/providers/s3/utils/client/runtime/xhrTransferHandler'; /** * S3 transfer handler for browser and React Native based on XHR. On top of basic transfer handler, it also supports @@ -23,7 +22,7 @@ import { xhrTransferHandler } from '../xhrTransferHandler'; * @internal */ export const s3TransferHandler = composeTransferHandler< - [{}, UserAgentOptions, RetryOptions, SigningOptions], + [object, UserAgentOptions, RetryOptions, SigningOptions], HttpRequest, HttpResponse, typeof xhrTransferHandler diff --git a/packages/storage/src/providers/s3/utils/client/runtime/xhrTransferHandler.ts b/packages/storage/src/providers/s3/utils/client/runtime/xhrTransferHandler.ts index 74c71a0d4e7..8f1c9185a9e 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/xhrTransferHandler.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/xhrTransferHandler.ts @@ -4,11 +4,14 @@ import { HttpRequest, HttpResponse, - TransferHandler, ResponseBodyMixin, + TransferHandler, withMemoization, } from '@aws-amplify/core/internals/aws-client-utils'; import { ConsoleLogger } from '@aws-amplify/core'; +import { TransferProgressEvent } from '~/src/types/common'; +import { CanceledError } from '~/src/errors/CanceledError'; + import { ABORT_ERROR_CODE, ABORT_ERROR_MESSAGE, @@ -17,8 +20,6 @@ import { NETWORK_ERROR_CODE, NETWORK_ERROR_MESSAGE, } from './constants'; -import { TransferProgressEvent } from '../../../../../types/common'; -import { CanceledError } from '../../../../../errors/CanceledError'; const logger = new ConsoleLogger('xhr-http-handler'); @@ -30,8 +31,8 @@ export interface XhrTransferHandlerOptions { // download binary data. Otherwise, use `text` to return the response as a string. responseType: 'text' | 'blob'; abortSignal?: AbortSignal; - onDownloadProgress?: (event: TransferProgressEvent) => void; - onUploadProgress?: (event: TransferProgressEvent) => void; + onDownloadProgress?(event: TransferProgressEvent): void; + onUploadProgress?(event: TransferProgressEvent): void; } /** @@ -80,7 +81,7 @@ export const xhrTransferHandler: TransferHandler< xhr.addEventListener('error', () => { const networkError = buildHandlerError( NETWORK_ERROR_MESSAGE, - NETWORK_ERROR_CODE + NETWORK_ERROR_CODE, ); logger.error(NETWORK_ERROR_MESSAGE); reject(networkError); @@ -111,24 +112,24 @@ export const xhrTransferHandler: TransferHandler< // The load event is triggered after the error/abort/load event. So we need to check if the xhr is null. if (!xhr) return; const responseHeaders = convertResponseHeaders( - xhr.getAllResponseHeaders() + xhr.getAllResponseHeaders(), ); - const responseType = xhr.responseType; + const { responseType: xhrResponseType } = xhr; const responseBlob = xhr.response as Blob; - const responseText = responseType === 'text' ? xhr.responseText : ''; + const responseText = xhrResponseType === 'text' ? xhr.responseText : ''; const bodyMixIn: ResponseBodyMixin = { blob: () => Promise.resolve(responseBlob), text: withMemoization(() => - responseType === 'blob' + xhrResponseType === 'blob' ? readBlobAsText(responseBlob) - : Promise.resolve(responseText) + : Promise.resolve(responseText), ), json: () => Promise.reject( // S3 does not support JSON response. So fail-fast here with nicer error message. new Error( - 'Parsing response to JSON is not implemented. Please use response.text() instead.' - ) + 'Parsing response to JSON is not implemented. Please use response.text() instead.', + ), ), }; const response: HttpResponse = { @@ -184,7 +185,7 @@ export const xhrTransferHandler: TransferHandler< }; const convertToTransferProgressEvent = ( - event: ProgressEvent + event: ProgressEvent, ): TransferProgressEvent => ({ transferredBytes: event.loaded, totalBytes: event.lengthComputable ? event.total : undefined, @@ -193,6 +194,7 @@ const convertToTransferProgressEvent = ( const buildHandlerError = (message: string, name: string): Error => { const error = new Error(message); error.name = name; + return error; }; @@ -205,6 +207,7 @@ const convertResponseHeaders = (xhrHeaders: string): Record => { if (!xhrHeaders) { return {}; } + return xhrHeaders .split('\r\n') .reduce((headerMap: Record, line: string) => { @@ -212,12 +215,14 @@ const convertResponseHeaders = (xhrHeaders: string): Record => { const header = parts.shift()!; const value = parts.join(': '); headerMap[header.toLowerCase()] = value; + return headerMap; }, {}); }; const readBlobAsText = (blob: Blob) => { const reader = new FileReader(); + return new Promise((resolve, reject) => { reader.onloadend = () => { if (reader.readyState !== FileReader.DONE) { diff --git a/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/dom.ts b/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/dom.ts index 89bf9da7304..f5b1d0a3a1c 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/dom.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/dom.ts @@ -12,6 +12,7 @@ export const parser = { const xml = domParser.parseFromString(xmlStr, 'text/xml'); const parsedObj = parseXmlNode(xml); const rootKey = Object.keys(parsedObj)[0]; + return parsedObj[rootKey]; }, }; @@ -31,13 +32,12 @@ const parseXmlNode = (node: Node): any => { // Node like foo will be converted to { Location: 'foo' } // instead of { Location: { '#text': 'foo' } }. if (isTextOnlyElementNode(node)) { - return node.childNodes[0]?.nodeValue!; + return node.childNodes[0].nodeValue!; } const nodeValue: Record = {}; // convert attributes - for (let i = 0; i < node.attributes.length; i++) { - const attr = node.attributes[i]; + for (const attr of node.attributes) { if (!isNamespaceAttributeName(attr.nodeName)) { nodeValue[attr.nodeName] = attr.nodeValue!; } @@ -45,8 +45,7 @@ const parseXmlNode = (node: Node): any => { // convert child nodes if (node.children.length > 0) { - for (let i = 0; i < node.children.length; i++) { - const child = node.children[i]; + for (const child of node.children) { const childValue = parseXmlNode(child); if (childValue === undefined) { continue; @@ -79,12 +78,12 @@ const isTextOnlyElementNode = (node: Element): boolean => node.firstChild?.nodeType === Node.TEXT_NODE; const hasOnlyNamespaceAttributes = (node: Element): boolean => { - for (let i = 0; i < node.attributes.length; i++) { - const attr = node.attributes[i]; + for (const attr of node.attributes) { if (!isNamespaceAttributeName(attr.nodeName)) { return false; } } + return true; }; diff --git a/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/pureJs.ts b/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/pureJs.ts index 61f5ce74b2e..dcfc9950485 100644 --- a/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/pureJs.ts +++ b/packages/storage/src/providers/s3/utils/client/runtime/xmlParser/pureJs.ts @@ -12,7 +12,7 @@ import { XMLParser } from 'fast-xml-parser'; */ export const parser = { parse: (xmlStr: string): any => { - const parser = new XMLParser({ + const xmlParser = new XMLParser({ attributeNamePrefix: '', htmlEntities: true, ignoreAttributes: false, @@ -23,9 +23,9 @@ export const parser = { tagValueProcessor: (_, val) => val.trim() === '' && val.includes('\n') ? '' : undefined, }); - parser.addEntity('#xD', '\r'); - parser.addEntity('#10', '\n'); - const parsedObj: any = parser.parse(xmlStr); + xmlParser.addEntity('#xD', '\r'); + xmlParser.addEntity('#10', '\n'); + const parsedObj: any = xmlParser.parse(xmlStr); const textNodeName = '#text'; const key = Object.keys(parsedObj)[0]; const parsedObjToReturn = parsedObj[key]; @@ -33,6 +33,7 @@ export const parser = { parsedObjToReturn[key] = parsedObjToReturn[textNodeName]; delete parsedObjToReturn[textNodeName]; } + return getValueFromTextNode(parsedObjToReturn); }, }; @@ -45,11 +46,15 @@ export const parser = { const getValueFromTextNode = (obj: any) => { const textNodeName = '#text'; for (const key in obj) { - if (obj.hasOwnProperty(key) && obj[key][textNodeName] !== undefined) { + if ( + Object.prototype.hasOwnProperty.call(obj, key) && + obj[key][textNodeName] !== undefined + ) { obj[key] = obj[key][textNodeName]; } else if (typeof obj[key] === 'object' && obj[key] !== null) { obj[key] = getValueFromTextNode(obj[key]); } } + return obj; }; diff --git a/packages/storage/src/providers/s3/utils/client/types.ts b/packages/storage/src/providers/s3/utils/client/types.ts index bada269fb21..e7105353cd8 100644 --- a/packages/storage/src/providers/s3/utils/client/types.ts +++ b/packages/storage/src/providers/s3/utils/client/types.ts @@ -112,8 +112,7 @@ declare const TaggingDirective: { * * The input for {@link AbortMultipartUploadCommand}. */ -export interface AbortMultipartUploadCommandInput - extends AbortMultipartUploadRequest {} +export type AbortMultipartUploadCommandInput = AbortMultipartUploadRequest /** * @public * @@ -183,8 +182,7 @@ export interface CommonPrefix { * * The input for {@link CompleteMultipartUploadCommand}. */ -export interface CompleteMultipartUploadCommandInput - extends CompleteMultipartUploadRequest {} +export type CompleteMultipartUploadCommandInput = CompleteMultipartUploadRequest /** * @public * @@ -429,7 +427,7 @@ export interface CompletedPart { * * The input for {@link CopyObjectCommand}. */ -export interface CopyObjectCommandInput extends CopyObjectRequest {} +export type CopyObjectCommandInput = CopyObjectRequest /** * @public * @@ -787,8 +785,7 @@ export interface CopyObjectResult { * * The input for {@link CreateMultipartUploadCommand}. */ -export interface CreateMultipartUploadCommandInput - extends CreateMultipartUploadRequest {} +export type CreateMultipartUploadCommandInput = CreateMultipartUploadRequest /** * @public * @@ -1045,7 +1042,7 @@ export interface CreateMultipartUploadRequest { * * The input for {@link DeleteObjectCommand}. */ -export interface DeleteObjectCommandInput extends DeleteObjectRequest {} +export type DeleteObjectCommandInput = DeleteObjectRequest /** * @public * @@ -1124,7 +1121,7 @@ export interface DeleteObjectRequest { * * The input for {@link GetObjectCommand}. */ -export interface GetObjectCommandInput extends GetObjectRequest {} +export type GetObjectCommandInput = GetObjectRequest /** * @public * @@ -1441,7 +1438,7 @@ export interface GetObjectRequest { * * The input for {@link HeadObjectCommand}. */ -export interface HeadObjectCommandInput extends HeadObjectRequest {} +export type HeadObjectCommandInput = HeadObjectRequest /** * @public * @@ -1790,7 +1787,7 @@ export interface Initiator { * * The input for {@link ListObjectsV2Command}. */ -export interface ListObjectsV2CommandInput extends ListObjectsV2Request {} +export type ListObjectsV2CommandInput = ListObjectsV2Request /** * @public * @@ -1948,7 +1945,7 @@ export interface ListObjectsV2Request { * * The input for {@link ListPartsCommand}. */ -export interface ListPartsCommandInput extends ListPartsRequest {} +export type ListPartsCommandInput = ListPartsRequest /** * @public * @@ -2202,7 +2199,7 @@ export interface Part { /** * This interface extends from `PutObjectRequest` interface. There are more parameters than `Body` defined in {@link PutObjectRequest} */ -export interface PutObjectCommandInput extends PutObjectCommandInputType {} +export type PutObjectCommandInput = PutObjectCommandInputType /** * @public * @@ -2540,7 +2537,7 @@ export interface PutObjectRequest { /** * This interface extends from `UploadPartRequest` interface. There are more parameters than `Body` defined in {@link UploadPartRequest} */ -export interface UploadPartCommandInput extends UploadPartCommandInputType {} +export type UploadPartCommandInput = UploadPartCommandInputType /** * @public * diff --git a/packages/storage/src/providers/s3/utils/client/uploadPart.ts b/packages/storage/src/providers/s3/utils/client/uploadPart.ts index e02ba2c57a4..a28ee2ccdae 100644 --- a/packages/storage/src/providers/s3/utils/client/uploadPart.ts +++ b/packages/storage/src/providers/s3/utils/client/uploadPart.ts @@ -14,17 +14,18 @@ import { import { composeServiceApi } from '@aws-amplify/core/internals/aws-client-utils/composers'; import { defaultConfig } from './base'; -import type { UploadPartCommandInput, UploadPartCommandOutput } from './types'; import { - buildStorageServiceError, - validateS3RequiredParameter, assignStringVariables, + buildStorageServiceError, map, parseXmlError, s3TransferHandler, serializePathnameObjectKey, + validateS3RequiredParameter, } from './utils'; +import type { UploadPartCommandInput, UploadPartCommandOutput } from './types'; + // Content-length is ignored here because it's forbidden header // and will be set by browser or fetch polyfill. export type UploadPartInput = Pick< @@ -39,7 +40,7 @@ export type UploadPartOutput = Pick< const uploadPartSerializer = async ( input: UploadPartInput, - endpoint: Endpoint + endpoint: Endpoint, ): Promise => { const headers = { ...assignStringVariables({ 'content-md5': input.ContentMD5 }), @@ -54,6 +55,7 @@ const uploadPartSerializer = async ( partNumber: input.PartNumber + '', uploadId: input.UploadId, }).toString(); + return { method: 'PUT', headers, @@ -63,7 +65,7 @@ const uploadPartSerializer = async ( }; const uploadPartDeserializer = async ( - response: HttpResponse + response: HttpResponse, ): Promise => { if (response.statusCode >= 300) { const error = (await parseXmlError(response)) as Error; @@ -82,5 +84,5 @@ export const uploadPart = composeServiceApi( s3TransferHandler, uploadPartSerializer, uploadPartDeserializer, - { ...defaultConfig, responseType: 'text' } + { ...defaultConfig, responseType: 'text' }, ); diff --git a/packages/storage/src/providers/s3/utils/client/utils/deserializeHelpers.ts b/packages/storage/src/providers/s3/utils/client/utils/deserializeHelpers.ts index 50e8de17e41..65717367627 100644 --- a/packages/storage/src/providers/s3/utils/client/utils/deserializeHelpers.ts +++ b/packages/storage/src/providers/s3/utils/client/utils/deserializeHelpers.ts @@ -3,8 +3,7 @@ import { Headers } from '@aws-amplify/core/internals/aws-client-utils'; import { ServiceError } from '@aws-amplify/core/internals/utils'; - -import { StorageError } from '../../../../../errors/StorageError'; +import { StorageError } from '~/src/errors/StorageError'; type PropertyNameWithStringValue = string; type PropertyNameWithSubsequentDeserializer = [string, (arg: any) => T]; @@ -48,9 +47,9 @@ type InferInstructionResultType> = * * @internal */ -export const map = }>( +export const map = >>( obj: Record, - instructions: Instructions + instructions: Instructions, ): { [K in keyof Instructions]: InferInstructionResultType; } => { @@ -59,12 +58,13 @@ export const map = }>( const [accessor, deserializer] = Array.isArray(instruction) ? instruction : [instruction]; - if (obj.hasOwnProperty(accessor)) { + if (Object.prototype.hasOwnProperty.call(obj, accessor)) { result[key as keyof Instructions] = deserializer ? deserializer(obj[accessor]) : String(obj[accessor]); } } + return result; }; @@ -108,16 +108,17 @@ export const deserializeTimestamp = (value: string): Date | undefined => { * * @internal */ -export const emptyArrayGuard = >( +export const emptyArrayGuard = ( value: any, - deserializer: (value: any[]) => T + deserializer: (value: any[]) => T, ): T => { if (value === '') { return [] as any as T; } const valueArray = (Array.isArray(value) ? value : [value]).filter( - e => e != null + e => e != null, ); + return deserializer(valueArray); }; @@ -125,15 +126,17 @@ export const emptyArrayGuard = >( * @internal */ export const deserializeMetadata = ( - headers: Headers + headers: Headers, ): Record => { const objectMetadataHeaderPrefix = 'x-amz-meta-'; const deserialized = Object.keys(headers) .filter(header => header.startsWith(objectMetadataHeaderPrefix)) .reduce((acc, header) => { acc[header.replace(objectMetadataHeaderPrefix, '')] = headers[header]; + return acc; }, {} as any); + return Object.keys(deserialized).length > 0 ? deserialized : undefined; }; @@ -144,7 +147,7 @@ export const deserializeMetadata = ( */ export const buildStorageServiceError = ( error: Error, - statusCode: number + statusCode: number, ): ServiceError => { const storageError = new StorageError({ name: error.name, @@ -154,5 +157,6 @@ export const buildStorageServiceError = ( storageError.recoverySuggestion = 'Please add the object with this key to the bucket as the key is not found.'; } + return storageError; }; diff --git a/packages/storage/src/providers/s3/utils/client/utils/parsePayload.ts b/packages/storage/src/providers/s3/utils/client/utils/parsePayload.ts index 7ac7029a8d1..312d78f695f 100644 --- a/packages/storage/src/providers/s3/utils/client/utils/parsePayload.ts +++ b/packages/storage/src/providers/s3/utils/client/utils/parsePayload.ts @@ -6,8 +6,7 @@ import { HttpResponse, parseMetadata, } from '@aws-amplify/core/internals/aws-client-utils'; - -import { parser } from '../runtime'; +import { parser } from '~/src/providers/s3/utils/client/runtime'; export const parseXmlError: ErrorParser = async (response?: HttpResponse) => { if (!response || response.statusCode < 300) { @@ -15,13 +14,14 @@ export const parseXmlError: ErrorParser = async (response?: HttpResponse) => { } const { statusCode } = response; const body = await parseXmlBody(response); - const code = body?.['Code'] + const code = body?.Code ? (body.Code as string) : statusCode === 404 ? 'NotFound' : statusCode.toString(); - const message = body?.['message'] ?? body?.['Message'] ?? code; + const message = body?.message ?? body?.Message ?? code; const error = new Error(message); + return Object.assign(error, { name: code, $metadata: parseMetadata(response), @@ -41,5 +41,6 @@ export const parseXmlBody = async (response: HttpResponse): Promise => { throw new Error('Failed to parse XML response.'); } } + return {}; }; diff --git a/packages/storage/src/providers/s3/utils/client/utils/serializeHelpers.ts b/packages/storage/src/providers/s3/utils/client/utils/serializeHelpers.ts index 1fd41bc916e..ad74ef331fa 100644 --- a/packages/storage/src/providers/s3/utils/client/utils/serializeHelpers.ts +++ b/packages/storage/src/providers/s3/utils/client/utils/serializeHelpers.ts @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { extendedEncodeURIComponent } from '@aws-amplify/core/internals/aws-client-utils'; import { AmplifyErrorCode } from '@aws-amplify/core/internals/utils'; -import { StorageError } from '../../../../../errors/StorageError'; +import { StorageError } from '~/src/errors/StorageError'; /** * @internal */ export const assignStringVariables = ( - values: Record string } | undefined> + values: Record, ): Record => { const queryParams: Record = {}; for (const [key, value] of Object.entries(values)) { @@ -16,6 +16,7 @@ export const assignStringVariables = ( queryParams[key] = value.toString(); } } + return queryParams; }; @@ -39,7 +40,7 @@ interface ObjectConfigs { * @internal */ export const serializeObjectConfigsToHeaders = async ( - input: ObjectConfigs + input: ObjectConfigs, ) => ({ ...assignStringVariables({ 'x-amz-acl': input.ACL, @@ -55,10 +56,11 @@ export const serializeObjectConfigsToHeaders = async ( }); const serializeMetadata = ( - metadata: Record = {} + metadata: Record = {}, ): Record => Object.keys(metadata).reduce((acc: any, suffix: string) => { acc[`x-amz-meta-${suffix.toLowerCase()}`] = metadata[suffix]; + return acc; }, {}); @@ -77,14 +79,14 @@ export const serializePathnameObjectKey = (url: URL, key: string) => { export function validateS3RequiredParameter( assertion: boolean, - paramName: string + paramName: string, ): asserts assertion { if (!assertion) { throw new StorageError({ name: AmplifyErrorCode.Unknown, message: 'An unknown error has occurred.', underlyingError: new TypeError( - `Expected a non-null value for S3 parameter ${paramName}` + `Expected a non-null value for S3 parameter ${paramName}`, ), recoverySuggestion: 'This is likely to be a bug. Please reach out to library authors.', diff --git a/packages/storage/src/providers/s3/utils/md5.native.ts b/packages/storage/src/providers/s3/utils/md5.native.ts index cdb6f63e210..94e85a84a67 100644 --- a/packages/storage/src/providers/s3/utils/md5.native.ts +++ b/packages/storage/src/providers/s3/utils/md5.native.ts @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { Md5 } from '@smithy/md5-js'; + import { toBase64 } from './client/utils'; export const calculateContentMd5 = async ( - content: Blob | string | ArrayBuffer | ArrayBufferView + content: Blob | string | ArrayBuffer | ArrayBufferView, ): Promise => { const hasher = new Md5(); if (typeof content === 'string') { @@ -19,6 +20,7 @@ export const calculateContentMd5 = async ( hasher.update(buffer); } const digest = await hasher.digest(); + return toBase64(digest); }; @@ -29,8 +31,8 @@ const readFile = (file: Blob): Promise => { if (reader.result) { resolve(reader.result as ArrayBuffer); } - reader.onabort = () => reject(new Error('Read aborted')); - reader.onerror = () => reject(reader.error); + reader.onabort = () => { reject(new Error('Read aborted')); }; + reader.onerror = () => { reject(reader.error); }; }; if (file !== undefined) reader.readAsArrayBuffer(file); }); diff --git a/packages/storage/src/providers/s3/utils/md5.ts b/packages/storage/src/providers/s3/utils/md5.ts index 430784f23c7..1a08c46ad5a 100644 --- a/packages/storage/src/providers/s3/utils/md5.ts +++ b/packages/storage/src/providers/s3/utils/md5.ts @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { Md5 } from '@smithy/md5-js'; + import { toBase64, utf8Encode } from './client/utils'; export const calculateContentMd5 = async ( - content: Blob | string | ArrayBuffer | ArrayBufferView + content: Blob | string | ArrayBuffer | ArrayBufferView, ): Promise => { const hasher = new Md5(); if (typeof content === 'string') { @@ -19,6 +20,7 @@ export const calculateContentMd5 = async ( hasher.update(utf8Encode(buffer)); } const digest = await hasher.digest(); + return toBase64(digest); }; @@ -30,12 +32,14 @@ const readFileToBase64 = (blob: Blob): Promise => { // response from readAsDataURL is always prepended with "data:*/*;base64," // reference: https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState if (reader.readyState !== 2) { - return reject(new Error('Reader aborted too early')); + reject(new Error('Reader aborted too early')); + + return; } resolve((reader.result as string).split(',')[1]); }; - reader.onabort = () => reject(new Error('Read aborted')); - reader.onerror = () => reject(reader.error); + reader.onabort = () => { reject(new Error('Read aborted')); }; + reader.onerror = () => { reject(reader.error); }; // reader.readAsArrayBuffer is not available in RN reader.readAsDataURL(blob); }); diff --git a/packages/storage/src/providers/s3/utils/resolveS3ConfigAndInput.ts b/packages/storage/src/providers/s3/utils/resolveS3ConfigAndInput.ts index cf32138b86e..5930c65bd8e 100644 --- a/packages/storage/src/providers/s3/utils/resolveS3ConfigAndInput.ts +++ b/packages/storage/src/providers/s3/utils/resolveS3ConfigAndInput.ts @@ -2,25 +2,26 @@ // SPDX-License-Identifier: Apache-2.0 import { AmplifyClassV6, StorageAccessLevel } from '@aws-amplify/core'; -import { assertValidationError } from '../../../errors/utils/assertValidationError'; -import { StorageValidationErrorCode } from '../../../errors/types/validation'; -import { StorageError } from '../../../errors/StorageError'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; +import { StorageError } from '~/src/errors/StorageError'; +import { resolvePrefix as defaultPrefixResolver } from '~/src/utils/resolvePrefix'; +import { ResolvedS3Config } from '~/src/providers/s3/types/options'; + import { DEFAULT_ACCESS_LEVEL, LOCAL_TESTING_S3_ENDPOINT } from './constants'; -import { resolvePrefix as defaultPrefixResolver } from '../../../utils/resolvePrefix'; -import { ResolvedS3Config } from '../types/options'; -type S3ApiOptions = { +interface S3ApiOptions { accessLevel?: StorageAccessLevel; targetIdentityId?: string; useAccelerateEndpoint?: boolean; -}; +} -type ResolvedS3ConfigAndInput = { +export interface ResolvedS3ConfigAndInput { s3Config: ResolvedS3Config; bucket: string; keyPrefix: string; isObjectLockEnabled?: boolean; -}; +} /** * resolve the common input options for S3 API handlers from Amplify configuration and library options. @@ -35,7 +36,7 @@ type ResolvedS3ConfigAndInput = { */ export const resolveS3ConfigAndInput = async ( amplify: AmplifyClassV6, - apiOptions?: S3ApiOptions + apiOptions?: S3ApiOptions, ): Promise => { // identityId is always cached in memory if forceRefresh is not set. So we can safely make calls here. const { credentials, identityId } = await amplify.Auth.fetchAuthSession({ @@ -43,7 +44,7 @@ export const resolveS3ConfigAndInput = async ( }); assertValidationError( !!credentials, - StorageValidationErrorCode.NoCredentials + StorageValidationErrorCode.NoCredentials, ); assertValidationError(!!identityId, StorageValidationErrorCode.NoIdentityId); diff --git a/packages/storage/src/providers/s3/utils/transferTask.ts b/packages/storage/src/providers/s3/utils/transferTask.ts index ecbd3aa3b30..8f2a0715e64 100644 --- a/packages/storage/src/providers/s3/utils/transferTask.ts +++ b/packages/storage/src/providers/s3/utils/transferTask.ts @@ -1,18 +1,18 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { isCancelError } from '../../../errors/CanceledError'; +import { isCancelError } from '~/src/errors/CanceledError'; import { DownloadTask, TransferTaskState, UploadTask, -} from '../../../types/common'; -import { logger } from '../../../utils'; +} from '~/src/types/common'; +import { logger } from '~/src/utils'; -type CreateCancellableTaskOptions = { - job: () => Promise; - onCancel: (message?: string) => void; -}; +interface CreateCancellableTaskOptions { + job(): Promise; + onCancel(message?: string): void; +} type CancellableTask = DownloadTask; @@ -21,12 +21,17 @@ const createCancellableTask = ({ onCancel, }: CreateCancellableTaskOptions): CancellableTask => { const state = 'IN_PROGRESS' as TransferTaskState; - let canceledErrorMessage: string | undefined = undefined; + let canceledErrorMessage: string | undefined; const cancelableTask = { cancel: (message?: string) => { - const { state } = cancelableTask; - if (state === 'CANCELED' || state === 'ERROR' || state === 'SUCCESS') { - logger.debug(`This task cannot be canceled. State: ${state}`); + const { state: currentState } = cancelableTask; + if ( + currentState === 'CANCELED' || + currentState === 'ERROR' || + currentState === 'SUCCESS' + ) { + logger.debug(`This task cannot be canceled. State: ${currentState}`); + return; } cancelableTask.state = 'CANCELED'; @@ -40,6 +45,7 @@ const createCancellableTask = ({ try { const result = await job(); cancelableTask.state = 'SUCCESS'; + return result; } catch (e) { if (isCancelError(e)) { @@ -58,13 +64,13 @@ const createCancellableTask = ({ export const createDownloadTask = createCancellableTask; -type CreateUploadTaskOptions = { - job: () => Promise; - onCancel: (message?: string) => void; - onResume?: () => void; - onPause?: () => void; +interface CreateUploadTaskOptions { + job(): Promise; + onCancel(message?: string): void; + onResume?(): void; + onPause?(): void; isMultipartUpload?: boolean; -}; +} export const createUploadTask = ({ job, @@ -83,8 +89,11 @@ export const createUploadTask = ({ const { state } = uploadTask; if (!isMultipartUpload || state !== 'IN_PROGRESS') { logger.debug(`This task cannot be paused. State: ${state}`); + return; } + // TODO(eslint): remove this linter suppression. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore uploadTask.state = 'PAUSED'; onPause?.(); @@ -93,8 +102,11 @@ export const createUploadTask = ({ const { state } = uploadTask; if (!isMultipartUpload || state !== 'PAUSED') { logger.debug(`This task cannot be resumed. State: ${state}`); + return; } + // TODO(eslint): remove this linter suppression. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore uploadTask.state = 'IN_PROGRESS'; onResume?.(); diff --git a/packages/storage/src/providers/s3/utils/userAgent.ts b/packages/storage/src/providers/s3/utils/userAgent.ts index 1779ddfd0c5..ece480b88eb 100644 --- a/packages/storage/src/providers/s3/utils/userAgent.ts +++ b/packages/storage/src/providers/s3/utils/userAgent.ts @@ -1,8 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { - StorageAction, Category, + StorageAction, getAmplifyUserAgent, } from '@aws-amplify/core/internals/utils'; diff --git a/packages/storage/src/types/common.ts b/packages/storage/src/types/common.ts index eaa4d546dfd..2f6815c08d3 100644 --- a/packages/storage/src/types/common.ts +++ b/packages/storage/src/types/common.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import type { CanceledError } from '../errors/CanceledError'; +import type { CanceledError } from '~/src/errors/CanceledError'; export type TransferTaskState = | 'IN_PROGRESS' @@ -10,12 +10,12 @@ export type TransferTaskState = | 'SUCCESS' | 'ERROR'; -export type TransferProgressEvent = { +export interface TransferProgressEvent { transferredBytes: number; totalBytes?: number; -}; +} -export type TransferTask = { +export interface TransferTask { /** * Cancel an ongoing transfer(upload/download) task. This will reject the `result` promise with an `AbortError` by * default. You can use `isCancelError` to check if the error is caused by cancellation. @@ -24,19 +24,19 @@ export type TransferTask = { * canceled. If provided, the `result` promise will be rejected with a {@link CanceledError} with supplied error * message instead. */ - cancel: (message?: string) => void; + cancel(message?: string): void; /** * Pause an ongoing transfer(upload/download) task. This method does not support the following scenarios: * * Downloading data or file from given key. */ - pause: () => void; + pause(): void; /** * Resume a paused transfer(upload/download) task. This method does not support the following scenarios: * * Downloading data or file from given key. */ - resume: () => void; + resume(): void; /** * Current state of the transfer task. @@ -47,7 +47,7 @@ export type TransferTask = { * Promise that resolves when the transfer task is completed. The promise will be rejected if the task is canceled. */ result: Promise; -}; +} export type DownloadTask = Omit< TransferTask, diff --git a/packages/storage/src/types/inputs.ts b/packages/storage/src/types/inputs.ts index 0a8e709137d..861ebf53876 100644 --- a/packages/storage/src/types/inputs.ts +++ b/packages/storage/src/types/inputs.ts @@ -2,30 +2,30 @@ // SPDX-License-Identifier: Apache-2.0 import { - StorageOptions, StorageListAllOptions, StorageListPaginateOptions, + StorageOptions, } from './options'; -export type StorageOperationInput = { +export interface StorageOperationInput { key: string; options?: Options; -}; +} export type StorageGetPropertiesInput = StorageOperationInput; -export type StorageRemoveInput = { +export interface StorageRemoveInput { key: string; options?: Options; -}; +} -export type StorageListInput< +export interface StorageListInput< Options extends StorageListAllOptions | StorageListPaginateOptions, -> = { +> { prefix?: string; options?: Options; -}; +} export type StorageGetUrlInput = StorageOperationInput; @@ -38,13 +38,13 @@ export type StorageUploadDataInput = data: StorageUploadDataPayload; }; -export type StorageCopyInput< +export interface StorageCopyInput< SourceOptions extends StorageOptions, DestinationOptions extends StorageOptions, -> = { +> { source: SourceOptions; destination: DestinationOptions; -}; +} /** * The data payload type for upload operation. diff --git a/packages/storage/src/types/options.ts b/packages/storage/src/types/options.ts index 9962084dfde..df992df5838 100644 --- a/packages/storage/src/types/options.ts +++ b/packages/storage/src/types/options.ts @@ -3,9 +3,9 @@ import { StorageAccessLevel } from '@aws-amplify/core'; -export type StorageOptions = { +export interface StorageOptions { accessLevel?: StorageAccessLevel; -}; +} export type StorageListAllOptions = StorageOptions & { listAll: true; diff --git a/packages/storage/src/types/outputs.ts b/packages/storage/src/types/outputs.ts index 766acf95365..b5b3a9236c0 100644 --- a/packages/storage/src/types/outputs.ts +++ b/packages/storage/src/types/outputs.ts @@ -3,7 +3,7 @@ import { ResponseBodyMixin } from '@aws-amplify/core/internals/aws-client-utils'; -export type StorageItem = { +export interface StorageItem { /** * Key of the object */ @@ -26,13 +26,13 @@ export type StorageItem = { * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html#UserMetadata */ metadata?: Record; -}; +} export type StorageDownloadDataOutput = T & { body: ResponseBodyMixin; }; -export type StorageGetUrlOutput = { +export interface StorageGetUrlOutput { /** * presigned URL of the given object. */ @@ -41,10 +41,10 @@ export type StorageGetUrlOutput = { * expiresAt is date in which generated URL expires. */ expiresAt: Date; -}; +} export type StorageUploadOutput = Item; -export type StorageListOutput = { +export interface StorageListOutput { items: Item[]; -}; +} diff --git a/packages/storage/src/utils/resolvePrefix.ts b/packages/storage/src/utils/resolvePrefix.ts index cba1973179d..fc3457647af 100644 --- a/packages/storage/src/utils/resolvePrefix.ts +++ b/packages/storage/src/utils/resolvePrefix.ts @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { StorageAccessLevel } from '@aws-amplify/core'; -import { assertValidationError } from '../errors/utils/assertValidationError'; -import { StorageValidationErrorCode } from '../errors/types/validation'; +import { assertValidationError } from '~/src/errors/utils/assertValidationError'; +import { StorageValidationErrorCode } from '~/src/errors/types/validation'; -type ResolvePrefixOptions = { +interface ResolvePrefixOptions { accessLevel: StorageAccessLevel; targetIdentityId?: string; -}; +} export const resolvePrefix = ({ accessLevel, @@ -17,14 +17,16 @@ export const resolvePrefix = ({ if (accessLevel === 'private') { assertValidationError( !!targetIdentityId, - StorageValidationErrorCode.NoIdentityId + StorageValidationErrorCode.NoIdentityId, ); + return `private/${targetIdentityId}/`; } else if (accessLevel === 'protected') { assertValidationError( !!targetIdentityId, - StorageValidationErrorCode.NoIdentityId + StorageValidationErrorCode.NoIdentityId, ); + return `protected/${targetIdentityId}/`; } else { return 'public/'; diff --git a/packages/storage/tsconfig.build.json b/packages/storage/tsconfig.build.json index af6adca185d..93dc82e4dc9 100644 --- a/packages/storage/tsconfig.build.json +++ b/packages/storage/tsconfig.build.json @@ -1,5 +1,4 @@ { - "extends": "../tsconfig.base.json", - "compilerOptions": {}, - "include": ["lib*/**/*.ts", "src"] + "extends": "./tsconfig.json", + "exclude": ["__tests__"] } diff --git a/packages/storage/tsconfig.json b/packages/storage/tsconfig.json index df37be20748..d0bdc6f1ce2 100644 --- a/packages/storage/tsconfig.json +++ b/packages/storage/tsconfig.json @@ -1,10 +1,12 @@ { - "extends": "../tsconfig.base.json", + "extends": "../../tsconfig.json", "compilerOptions": { - "importHelpers": true, - "strict": true, - "noImplicitAny": true, - "resolveJsonModule": true + "rootDirs": ["src"], + "rootDirs": ["src"], + "baseUrl": ".", + "paths": { + "~/*": ["./*"] + } }, - "include": ["./src"] + "include": ["./src", "__tests__"] } diff --git a/packages/storage/tsconfig.test.json b/packages/storage/tsconfig.test.json new file mode 100644 index 00000000000..ab13dce0bd9 --- /dev/null +++ b/packages/storage/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + // TODO(test): enable noImplicitAny for tests + "noImplicitAny": false + } +} diff --git a/packages/storage/tsconfig.watch.json b/packages/storage/tsconfig.watch.json new file mode 100644 index 00000000000..976ad51ea8c --- /dev/null +++ b/packages/storage/tsconfig.watch.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "watchOptions": { + "excludeDirectories": ["dist*"] + } +} diff --git a/packages/storage/tslint.json b/packages/storage/tslint.json deleted file mode 100644 index 7fac7237f19..00000000000 --- a/packages/storage/tslint.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "linterOptions": { - "exclude": ["src/providers/s3/utils/client/types.ts"] - }, - "defaultSeverity": "error", - "plugins": ["prettier"], - "extends": [], - "jsRules": {}, - "rules": { - "prefer-const": true, - "max-line-length": [ - true, - { - "limit": 120, - "ignore-pattern": "//" - } - ], - "no-var-keyword": true, - "object-literal-shorthand": true, - "no-eval": true, - "space-before-function-paren": [ - true, - { - "anonymous": "ignore", - "named": "never" - } - ], - "no-parameter-reassignment": true, - "align": [true, "parameters"], - "no-duplicate-imports": true, - "one-variable-per-declaration": [false, "ignore-for-loop"], - "triple-equals": [true, "allow-null-check"], - "comment-format": [true, "check-space"], - "indent": [false], - "whitespace": [ - false, - "check-branch", - "check-decl", - "check-operator", - "check-preblock" - ], - "eofline": true, - "variable-name": [ - true, - "check-format", - "allow-pascal-case", - "allow-snake-case", - "allow-leading-underscore" - ], - "semicolon": [true, "always", "ignore-interfaces"] - }, - "rulesDirectory": [] -} diff --git a/packages/tsconfig.base.json b/packages/tsconfig.base.json deleted file mode 100644 index 1c06ec0456d..00000000000 --- a/packages/tsconfig.base.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "esModuleInterop": true, - "noImplicitAny": false, - "lib": [ - "dom", - "dom.iterable", - "es2017", - "esnext.asynciterable", - "es2018.asyncgenerator", - "es2019", - "es2020.promise" - ], - "downlevelIteration": true, - "target": "es2020", - "moduleResolution": "node", - "declaration": true, - "noEmitOnError": true, - "incremental": true, - "importHelpers": true, - "skipLibCheck": true, - "types": ["node"] - } -} diff --git a/prettier.config.js b/prettier.config.js index e2b1a6a467c..b721b05ad0e 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,6 +1,7 @@ /** @type {import('prettier').Config} */ module.exports = { - trailingComma: 'es5', + trailingComma: 'all', + printWidth: 80, singleQuote: true, useTabs: true, arrowParens: 'avoid', diff --git a/rollup/common.mjs b/rollup/common.mjs index a054b04f82a..4df8491b754 100644 --- a/rollup/common.mjs +++ b/rollup/common.mjs @@ -1,7 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -const defaultTSConfigPath = './tsconfig.json'; +import tsCompiler from 'ts-patch/compiler/typescript.js'; +const defaultTSConfigPath = './tsconfig.build.json'; /** @type {import("rollup").OutputOptions}*/ export const cjsOutput = { @@ -20,6 +21,9 @@ export const cjsTSOptions = { sourceMap: false, tsconfig: defaultTSConfigPath, tsBuildInfoFile: 'dist/meta/cjs.tsbuildinfo', + // Use ts-patch to transform ts files to leverage the tsconfig plugins + // programmatically. + typescript: tsCompiler, }; /** @type {import("rollup").OutputOptions}*/ @@ -38,4 +42,7 @@ export const esmTSOptions = { sourceMap: false, tsconfig: defaultTSConfigPath, tsBuildInfoFile: 'dist/meta/cjs.tsbuildinfo', + // Use ts-patch to transform ts files to leverage the tsconfig plugins + // programmatically. + typescript: tsCompiler, }; diff --git a/scripts/test-github-actions.js b/scripts/test-github-actions.js index d6523d5f498..dd0692de846 100755 --- a/scripts/test-github-actions.js +++ b/scripts/test-github-actions.js @@ -2,13 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 'use strict'; -const glob = require('glob'); const fs = require('fs'); + +const glob = require('glob'); const yaml = require('js-yaml'); function parseYamlFile(file) { const fileContents = fs.readFileSync(file, 'utf8'); - return yaml.safeLoad(fileContents); + + return yaml.load(fileContents); } function getKeyValuesFor(targetKey, yamlObject) { @@ -62,7 +64,7 @@ for (const file of [...workflowYmlFiles, ...actionYmlFiles]) { continue; } console.log( - `In ${file} the uses reference ${val} must either be local to the project or fully reference a specific action commit on an external project` + `In ${file} the uses reference ${val} must either be local to the project or fully reference a specific action commit on an external project`, ); exitCode = 1; } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..b2e4a76bb6b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "declaration": true, + "downlevelIteration": true, + "esModuleInterop": true, + "importHelpers": true, + "incremental": true, + "lib": [ + "dom", + "dom.iterable", + "es2017", + "esnext.asynciterable", + "es2018.asyncgenerator", + "es2019", + "es2020.promise" + ], + "moduleResolution": "node", + "noEmitOnError": true, + "noImplicitAny": true, + "skipLibCheck": true, + "strict": true, + "sourceMap": true, + "resolveJsonModule": true, + "target": "es2020", + "module": "es2020", + "types": ["node", "jest"], + "plugins": [ + { "transform": "typescript-transform-paths", "useRootDirs": true }, + { + "transform": "typescript-transform-paths", + "useRootDirs": true, + "afterDeclarations": true + } + ] + }, + "exclude": ["node_modules", "dist", ".eslintrc.js", ".eslintrc.scoped.js"] +} diff --git a/yarn.lock b/yarn.lock index 4a3596cd852..b13aceea59e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@ampproject/remapping@^2.0.0", "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -982,7 +987,7 @@ json5 "^2.1.2" semver "^6.3.0" -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.0", "@babel/core@^7.20.0": +"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.0", "@babel/core@^7.20.0": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9" integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== @@ -1003,7 +1008,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.17.0", "@babel/generator@^7.20.0", "@babel/generator@^7.23.3", "@babel/generator@^7.23.4", "@babel/generator@^7.7.2": +"@babel/generator@^7.14.0", "@babel/generator@^7.17.0", "@babel/generator@^7.20.0", "@babel/generator@^7.23.3", "@babel/generator@^7.23.4", "@babel/generator@^7.4.0", "@babel/generator@^7.7.2": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.4.tgz#4a41377d8566ec18f807f42962a7f3551de83d1c" integrity sha512-esuS49Cga3HcThFNebGhlgsrVLkvhqvYDTzgjfFFlHJcIfLe5jFmRRfCQ1KuBfc4Jrtn3ndLgKWAKjBE+IraYQ== @@ -1211,7 +1216,7 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.17.0", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3", "@babel/parser@^7.23.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.17.0", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.3", "@babel/parser@^7.23.4", "@babel/parser@^7.4.3": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.4.tgz#409fbe690c333bb70187e2de4021e1e47a026661" integrity sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ== @@ -2092,7 +2097,7 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.22.15", "@babel/template@^7.3.3": +"@babel/template@^7.0.0", "@babel/template@^7.16.7", "@babel/template@^7.22.15", "@babel/template@^7.3.3", "@babel/template@^7.4.0": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== @@ -2101,7 +2106,7 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.17.0", "@babel/traverse@^7.20.0", "@babel/traverse@^7.23.3", "@babel/traverse@^7.23.4": +"@babel/traverse@^7.14.0", "@babel/traverse@^7.17.0", "@babel/traverse@^7.20.0", "@babel/traverse@^7.23.3", "@babel/traverse@^7.23.4", "@babel/traverse@^7.4.3": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.4.tgz#c2790f7edf106d059a0098770fe70801417f3f85" integrity sha512-IYM8wSUwunWTB6tFC2dkKZhxbIjHoWemdK+3f8/wq8aKhbUscxD5MX72ubd90fxvFknaLPeGw5ycU84V1obHJg== @@ -2117,7 +2122,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.17.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.23.4", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.17.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.23.4", "@babel/types@^7.3.3", "@babel/types@^7.4.0", "@babel/types@^7.4.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.4.tgz#7206a1810fc512a7f7f7d4dace4cb4c1c9dbfb8e" integrity sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ== @@ -2131,6 +2136,14 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -2160,6 +2173,47 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/natural-compare/-/natural-compare-1.1.0.tgz#75f0642ad64701ffa9d42f1d7ada3b83f4e67cf3" integrity sha512-yuctPJs5lRXoI8LkpVZGAV6n+DKOuEsfpfcIDQ8ZjWHwazqk1QjBc4jMlof0UlZHyUqv4dwsOTooMiAmtzvwXA== +"@es-joy/jsdoccomment@~0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.41.0.tgz#4a2f7db42209c0425c71a1476ef1bdb6dcd836f6" + integrity sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw== + dependencies: + comment-parser "1.4.1" + esquery "^1.5.0" + jsdoc-type-pratt-parser "~4.0.0" + +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.3.tgz#797470a75fe0fbd5a53350ee715e85e87baff22d" + integrity sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.53.0": + version "8.53.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.53.0.tgz#bea56f2ed2b5baea164348ff4d5a879f6f81f20d" + integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w== + "@expo/config-plugins@^4.0.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-4.1.5.tgz#9d357d2cda9c095e511b51583ede8a3b76174068" @@ -2220,25 +2274,6 @@ pouchdb-collections "^1.0.1" tiny-queue "^0.2.1" -"@fimbul/bifrost@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/bifrost/-/bifrost-0.21.0.tgz#d0fafa25938fda475657a6a1e407a21bbe02c74e" - integrity sha512-ou8VU+nTmOW1jeg+FT+sn+an/M0Xb9G16RucrfhjXGWv1Q97kCoM5CG9Qj7GYOSdu7km72k7nY83Eyr53Bkakg== - dependencies: - "@fimbul/ymir" "^0.21.0" - get-caller-file "^2.0.0" - tslib "^1.8.1" - tsutils "^3.5.0" - -"@fimbul/ymir@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@fimbul/ymir/-/ymir-0.21.0.tgz#8525726787aceeafd4e199472c0d795160b5d4a1" - integrity sha512-T/y7WqPsm4n3zhT08EpB5sfdm2Kvw3gurAxr2Lr5dQeLi8ZsMlNT/Jby+ZmuuAAd1PnXYzKp+2SXgIkQIIMCUg== - dependencies: - inversify "^5.0.0" - reflect-metadata "^0.1.12" - tslib "^1.8.1" - "@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -2256,6 +2291,25 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== + dependencies: + "@humanwhocodes/object-schema" "^2.0.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== + "@hutson/parse-repository-url@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" @@ -2302,6 +2356,15 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -2387,6 +2450,15 @@ expect "^29.7.0" jest-snapshot "^29.7.0" +"@jest/fake-timers@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" + integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== + dependencies: + "@jest/types" "^24.9.0" + jest-message-util "^24.9.0" + jest-mock "^24.9.0" + "@jest/fake-timers@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" @@ -2446,6 +2518,15 @@ dependencies: "@sinclair/typebox" "^0.27.8" +"@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + "@jest/source-map@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" @@ -2455,6 +2536,15 @@ callsites "^3.0.0" graceful-fs "^4.2.9" +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@jest/test-result@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" @@ -2475,6 +2565,28 @@ jest-haste-map "^29.7.0" slash "^3.0.0" +"@jest/transform@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" + integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^24.9.0" + babel-plugin-istanbul "^5.1.0" + chalk "^2.0.1" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.1.15" + jest-haste-map "^24.9.0" + jest-regex-util "^24.9.0" + jest-util "^24.9.0" + micromatch "^3.1.10" + pirates "^4.0.1" + realpath-native "^1.1.0" + slash "^2.0.0" + source-map "^0.6.1" + write-file-atomic "2.4.1" + "@jest/transform@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" @@ -2496,6 +2608,15 @@ slash "^3.0.0" write-file-atomic "^4.0.2" +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -2802,7 +2923,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -4477,6 +4598,43 @@ "@statoscope/types" "5.28.1" "@types/md5" "^2.3.2" +"@stylistic/eslint-plugin-js@1.4.0", "@stylistic/eslint-plugin-js@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.4.0.tgz#2934b8b58bbff7c68222727d7ed6542e524e23c4" + integrity sha512-cANyn4ECWu8kxPmBM4K/Q4WocD3JbA0POmGbA2lJ4tynPE8jGyKpfP8SZj6BIidXV0pkyqvxEfaKppB4D16UsA== + dependencies: + acorn "^8.11.2" + escape-string-regexp "^4.0.0" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + graphemer "^1.4.0" + +"@stylistic/eslint-plugin-jsx@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-1.4.0.tgz#82c7c1a81b0b67c787e28e8c135052bc2f089fd7" + integrity sha512-MB5MW8HnRm0nAeUpgVzr4NOzLtxWYBIBtW9iDXopykl1ZJOm/0LlSFlsw9wsXd4Zqarkow6IrV18HcZ0Hc06yQ== + dependencies: + "@stylistic/eslint-plugin-js" "^1.4.0" + estraverse "^5.3.0" + +"@stylistic/eslint-plugin-ts@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.4.0.tgz#881dcc2c881eb6fcd3417d3dfa143263139772a4" + integrity sha512-eNEC0MufXfe2v9fW+g5yDzMMcws80cn1gKIt9CaLVjUuWnwCdY4SG1hUtDfEpBCTNBZgG/LKKls15dSa1x++0g== + dependencies: + "@stylistic/eslint-plugin-js" "1.4.0" + "@typescript-eslint/utils" "^6.11.0" + graphemer "^1.4.0" + +"@stylistic/eslint-plugin@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin/-/eslint-plugin-1.4.0.tgz#d6845e7a2e14758b6d9957bc342b732240fa6bc1" + integrity sha512-DtPiS4jr7m+A2nlcn8Ls4LEsOj1KC8x+KvAmoUI+nTcnin4Hkb8/I9Vteuu2CFLyVmlnKIQhrL5JC/xUEMyE9w== + dependencies: + "@stylistic/eslint-plugin-js" "1.4.0" + "@stylistic/eslint-plugin-jsx" "1.4.0" + "@stylistic/eslint-plugin-ts" "1.4.0" + "@swc/helpers@0.5.2": version "0.5.2" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d" @@ -4532,7 +4690,7 @@ resolved "https://registry.yarnpkg.com/@types/archy/-/archy-0.0.32.tgz#8b572741dad9172dfbf289397af1bb41296d3e40" integrity sha512-5ZZ5+YGmUE01yejiXsKnTcvhakMZ2UllZlMsQni53Doc1JWhe21ia8VntRoRD6fAEWw08JBh/z9qQHJ+//MrIg== -"@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.0", "@types/babel__core@^7.1.14": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -4633,6 +4791,14 @@ dependencies: "@types/istanbul-lib-coverage" "*" +"@types/istanbul-reports@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" + integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== + dependencies: + "@types/istanbul-lib-coverage" "*" + "@types/istanbul-lib-report" "*" + "@types/istanbul-reports@^3.0.0": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" @@ -4662,11 +4828,16 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": +"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/lodash@4.14.182": version "4.14.182" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" @@ -4779,11 +4950,21 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.7.tgz#d62f1bd54724c84089f51f9218393930ba4abcf4" integrity sha512-8g25Nl3AuB1KulTlSUsUhUo/oBgBU6XIXQ+XURpeioEbEJvkO7qI4vDfREv3vJYHHzqXjcAHvoJy4pTtSQNZtA== +"@types/semver@^7.5.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35" + integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg== + "@types/semver@^7.5.1": version "7.5.6" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== +"@types/stack-utils@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" + integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== + "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -4823,6 +5004,13 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== +"@types/yargs@^13.0.0": + version "13.0.12" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.12.tgz#d895a88c703b78af0465a9de88aa92c61430b092" + integrity sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ== + dependencies: + "@types/yargs-parser" "*" + "@types/yargs@^15.0.0": version "15.0.19" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.19.tgz#328fb89e46109ecbdb70c295d96ff2f46dfd01b9" @@ -4844,6 +5032,143 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz#52aae65174ff526576351f9ccd41cea01001463f" + integrity sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.11.0" + "@typescript-eslint/type-utils" "6.11.0" + "@typescript-eslint/utils" "6.11.0" + "@typescript-eslint/visitor-keys" "6.11.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.11.0.tgz#9640d9595d905f3be4f278bf515130e6129b202e" + integrity sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ== + dependencies: + "@typescript-eslint/scope-manager" "6.11.0" + "@typescript-eslint/types" "6.11.0" + "@typescript-eslint/typescript-estree" "6.11.0" + "@typescript-eslint/visitor-keys" "6.11.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz#621f603537c89f4d105733d949aa4d55eee5cea8" + integrity sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A== + dependencies: + "@typescript-eslint/types" "6.11.0" + "@typescript-eslint/visitor-keys" "6.11.0" + +"@typescript-eslint/scope-manager@6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.12.0.tgz#5833a16dbe19cfbad639d4d33bcca5e755c7044b" + integrity sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw== + dependencies: + "@typescript-eslint/types" "6.12.0" + "@typescript-eslint/visitor-keys" "6.12.0" + +"@typescript-eslint/type-utils@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz#d0b8b1ab6c26b974dbf91de1ebc5b11fea24e0d1" + integrity sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA== + dependencies: + "@typescript-eslint/typescript-estree" "6.11.0" + "@typescript-eslint/utils" "6.11.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.11.0.tgz#8ad3aa000cbf4bdc4dcceed96e9b577f15e0bf53" + integrity sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA== + +"@typescript-eslint/types@6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.12.0.tgz#ffc5297bcfe77003c8b7b545b51c2505748314ac" + integrity sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q== + +"@typescript-eslint/typescript-estree@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz#7b52c12a623bf7f8ec7f8a79901b9f98eb5c7990" + integrity sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ== + dependencies: + "@typescript-eslint/types" "6.11.0" + "@typescript-eslint/visitor-keys" "6.11.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/typescript-estree@6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.12.0.tgz#764ccc32598549e5b48ec99e3b85f89b1385310c" + integrity sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw== + dependencies: + "@typescript-eslint/types" "6.12.0" + "@typescript-eslint/visitor-keys" "6.12.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.11.0.tgz#11374f59ef4cea50857b1303477c08aafa2ca604" + integrity sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.11.0" + "@typescript-eslint/types" "6.11.0" + "@typescript-eslint/typescript-estree" "6.11.0" + semver "^7.5.4" + +"@typescript-eslint/utils@^6.11.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.12.0.tgz#c6ce8c06fe9b0212620e5674a2036f6f8f611754" + integrity sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.12.0" + "@typescript-eslint/types" "6.12.0" + "@typescript-eslint/typescript-estree" "6.12.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.11.0": + version "6.11.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz#d991538788923f92ec40d44389e7075b359f3458" + integrity sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ== + dependencies: + "@typescript-eslint/types" "6.11.0" + eslint-visitor-keys "^3.4.1" + +"@typescript-eslint/visitor-keys@6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.12.0.tgz#5877950de42a0f3344261b7a1eee15417306d7e9" + integrity sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw== + dependencies: + "@typescript-eslint/types" "6.12.0" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": version "1.11.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" @@ -5076,12 +5401,17 @@ acorn-import-assertions@^1.9.0: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn-walk@^8.0.0, acorn-walk@^8.0.2: version "8.3.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== -acorn@^8.0.4, acorn@^8.1.0, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.11.2, acorn@^8.7.1, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== @@ -5208,6 +5538,14 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -5231,6 +5569,11 @@ archy@~1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== + are-we-there-yet@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" @@ -5277,6 +5620,29 @@ argv@0.0.2: resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" integrity sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw== +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" @@ -5287,11 +5653,82 @@ 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.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.reduce@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.6.tgz#63149931808c5fc1e1354814923d92d45f7d96d5" + integrity sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -5307,6 +5744,11 @@ asap@~2.0.6: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + ast-types@0.15.2: version "0.15.2" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d" @@ -5339,6 +5781,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + axios@^1.0.0: version "1.6.2" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" @@ -5353,6 +5800,19 @@ babel-core@^7.0.0-bridge.0: resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== +babel-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" + integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== + dependencies: + "@jest/transform" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/babel__core" "^7.1.0" + babel-plugin-istanbul "^5.1.0" + babel-preset-jest "^24.9.0" + chalk "^2.4.2" + slash "^2.0.0" + babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -5376,6 +5836,16 @@ babel-loader@^8.3.0: make-dir "^3.1.0" schema-utils "^2.6.5" +babel-plugin-istanbul@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854" + integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + find-up "^3.0.0" + istanbul-lib-instrument "^3.3.0" + test-exclude "^5.2.3" + babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -5387,6 +5857,13 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" +babel-plugin-jest-hoist@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" + integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw== + dependencies: + "@types/babel__traverse" "^7.0.6" + babel-plugin-jest-hoist@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" @@ -5495,6 +5972,14 @@ babel-preset-fbjs@^3.4.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" +babel-preset-jest@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" + integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== + dependencies: + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + babel-plugin-jest-hoist "^24.9.0" + babel-preset-jest@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" @@ -5523,6 +6008,19 @@ base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.3.1, base64-j resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -5553,6 +6051,13 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -5596,6 +6101,22 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -5664,12 +6185,7 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== - -builtin-modules@^3.1.0: +builtin-modules@^3.1.0, builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== @@ -5679,7 +6195,7 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ== -builtins@^5.0.0: +builtins@^5.0.0, builtins@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== @@ -5774,7 +6290,22 @@ cacache@^17.0.0, cacache@^17.0.4: tar "^6.1.11" unique-filename "^3.0.0" -call-bind@^1.0.2: +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== @@ -5836,6 +6367,13 @@ caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001541: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz#aa68a64188903e98f36eb9c56e48fba0c1fe2a32" integrity sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw== +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + chalk@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" @@ -5855,7 +6393,7 @@ chalk@^1.0.0: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.3.0, chalk@^2.4.2: +chalk@^2.0.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5927,6 +6465,16 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + classnames@^2.2.6: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" @@ -6090,6 +6638,14 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + color-convert@^1.9.0, color-convert@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -6183,7 +6739,7 @@ commander@^10.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== -commander@^2.11.0, commander@^2.12.1, commander@^2.20.0: +commander@^2.11.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -6213,6 +6769,11 @@ commander@~2.13.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== +comment-parser@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.1.tgz#bdafead37961ac079be11eb7ec65c4d021eaf9cc" + integrity sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg== + common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" @@ -6231,6 +6792,11 @@ compare-func@^2.0.0: array-ify "^1.0.0" dot-prop "^5.1.0" +component-emitter@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -6364,7 +6930,7 @@ conventional-recommended-bump@7.0.1: git-semver-tags "^5.0.0" meow "^8.1.2" -convert-source-map@^1.1.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -6379,6 +6945,11 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + core-js-compat@^3.31.0, core-js-compat@^3.33.1: version "3.33.3" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.33.3.tgz#ec678b772c5a2d8a7c60a91c3a81869aa704ae01" @@ -6424,6 +6995,13 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + cross-fetch@^3.0.4: version "3.1.8" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" @@ -6442,7 +7020,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -6517,20 +7095,27 @@ debounce@^1.2.1: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2.6.9, debug@^2.2.0: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -6576,12 +7161,17 @@ deep-equal@^1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.5.1" +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + deepmerge@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== -deepmerge@^4.2.2, deepmerge@^4.3.0: +deepmerge@^4.2.2, deepmerge@^4.3.0, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== @@ -6607,7 +7197,7 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3, define-properties@^1.2.0: +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -6616,6 +7206,28 @@ define-properties@^1.1.3, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + del@^6.0.0: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -6708,11 +7320,6 @@ diff-sequences@^29.6.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -6720,13 +7327,19 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523" - integrity sha512-qiB/Rir6Un6Ad/TIgTRzsremsTGWzs8j7woXvp14jgq00676uBiBT5eUOi+FgRywZFVy5Us/c04ISRpZhRbS6w== +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: - esutils "^1.1.6" - isarray "0.0.1" + esutils "^2.0.2" domexception@^1.0.1: version "1.0.1" @@ -6840,7 +7453,7 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: +enhanced-resolve@^5.0.0, enhanced-resolve@^5.12.0, enhanced-resolve@^5.15.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -6902,14 +7515,89 @@ errorhandler@^1.5.0, errorhandler@^1.5.1: accepts "~1.3.7" escape-html "~1.0.3" +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + es-module-lexer@^1.2.1: version "1.4.1" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" +es-set-tostringtag@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== + dependencies: + get-intrinsic "^1.2.2" + has-tostringtag "^1.0.0" + hasown "^2.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-html@~1.0.3: @@ -6943,6 +7631,129 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== + +eslint-config-standard@^17.1.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" + integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== + +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-import-resolver-typescript@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" + integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" + get-tsconfig "^4.5.0" + is-core-module "^2.11.0" + is-glob "^4.0.3" + +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-es-x@^7.1.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.3.0.tgz#c699280ad35cd315720c3cccf0fe503092c08788" + integrity sha512-W9zIs+k00I/I13+Bdkl/zG1MEO07G97XjUSQuH117w620SJ6bHtLUmoMvkGA2oYnI/gNdr+G7BONLyYnFaLLEQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.6.0" + +eslint-plugin-import@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz#8133232e4329ee344f2f612885ac3073b0b7e155" + integrity sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.14.2" + +eslint-plugin-jsdoc@^46.9.0: + version "46.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.9.0.tgz#9887569dbeef0a008a2770bfc5d0f7fc39f21f2b" + integrity sha512-UQuEtbqLNkPf5Nr/6PPRCtr9xypXY+g8y/Q7gPa0YK7eDhh0y2lWprXRnaYbW7ACgIUvpDKy9X2bZqxtGzBG9Q== + dependencies: + "@es-joy/jsdoccomment" "~0.41.0" + are-docs-informative "^0.0.2" + comment-parser "1.4.1" + debug "^4.3.4" + escape-string-regexp "^4.0.0" + esquery "^1.5.0" + is-builtin-module "^3.2.1" + semver "^7.5.4" + spdx-expression-parse "^3.0.1" + +eslint-plugin-n@^16.3.1: + version "16.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.3.1.tgz#6cd377d1349fed10854b6535392e91fb4123193b" + integrity sha512-w46eDIkxQ2FaTHcey7G40eD+FhTXOdKudDXPUO2n9WNcslze/i/HT2qJ3GXjHngYSGDISIgPNhwGtgoix4zeOw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + builtins "^5.0.1" + eslint-plugin-es-x "^7.1.0" + get-tsconfig "^4.7.0" + ignore "^5.2.4" + is-builtin-module "^3.2.1" + is-core-module "^2.12.1" + minimatch "^3.1.2" + resolve "^1.22.2" + semver "^7.5.3" + +eslint-plugin-no-relative-import-paths@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-relative-import-paths/-/eslint-plugin-no-relative-import-paths-1.5.3.tgz#8c7fa79aa5ba10da0d8d79ce973c6db37385cb0d" + integrity sha512-z7c7Km1U0zdLyPziWeRKSsN2mPaGaBHDjfXn98B8XjRIhFi2bPqduRYcxWih1kI5al5tQtiChXVmspLkB0wNsQ== + +eslint-plugin-promise@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" + integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== + +eslint-plugin-unused-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.0.0.tgz#d25175b0072ff16a91892c3aa72a09ca3a9e69e7" + integrity sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -6951,11 +7762,84 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.53.0: + version "8.53.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.53.0.tgz#14f2c8244298fcae1f46945459577413ba2697ce" + integrity sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.3" + "@eslint/js" "8.53.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + 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.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + 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.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + 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.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.4.2, esquery@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -6968,7 +7852,7 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.2.0: +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -6983,11 +7867,6 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -esutils@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.1.6.tgz#c01ccaa9ae4b897c6d0c3e210ae52f3c7a844375" - integrity sha512-RG1ZkUT7iFJG9LSHr7KDuuMSlujfeTtMNIcInURxKAxhMtwQhI3NrQhz26gZQYlsYZQKzsnwtpKrFKj9K9Qu1A== - esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -7013,6 +7892,11 @@ events@^3.2.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + execa@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" @@ -7071,6 +7955,19 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -7102,6 +7999,21 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -7125,6 +8037,20 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + fake-indexeddb@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-4.0.2.tgz#e7a884158fa576e00f03e973b9874619947013e4" @@ -7137,12 +8063,12 @@ fast-base64-decode@^1.0.0: resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@3, fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@3, fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -7169,6 +8095,11 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-sta resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + fast-url-parser@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" @@ -7241,6 +8172,18 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + file-url@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" @@ -7253,6 +8196,16 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -7344,11 +8297,25 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + flow-enums-runtime@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/flow-enums-runtime/-/flow-enums-runtime-0.0.5.tgz#95884bfcc82edaf27eef7e1dd09732331cfbafbc" @@ -7379,6 +8346,18 @@ follow-redirects@^1.15.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -7396,6 +8375,13 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -7468,6 +8454,14 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" @@ -7478,6 +8472,16 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -7545,12 +8549,12 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.0, get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== @@ -7597,6 +8601,26 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-tsconfig@^4.5.0, get-tsconfig@^4.7.0: + version "4.7.2" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" + integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== + dependencies: + resolve-pkg-maps "^1.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + getenv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/getenv/-/getenv-1.0.0.tgz#874f2e7544fbca53c7a4738f37de8605c3fcfc31" @@ -7661,6 +8685,13 @@ glob-parent@5.1.2, glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -7701,7 +8732,7 @@ glob@^10.2.2, glob@^10.3.10: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: +glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -7734,11 +8765,34 @@ glob@^9.2.0: minipass "^4.2.4" path-scurry "^1.6.1" +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^13.19.0: + version "13.23.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globby@11.1.0, globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -7777,6 +8831,11 @@ graceful-fs@4.2.11, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + graphql@15.8.0: version "15.8.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" @@ -7818,6 +8877,11 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -7857,6 +8921,37 @@ has-unicode@2.0.1, has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + hasown@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" @@ -8037,7 +9132,7 @@ ignore-walk@^6.0.0: dependencies: minimatch "^9.0.0" -ignore@^5.0.4, ignore@^5.1.1, ignore@^5.1.2, ignore@^5.2.0: +ignore@^5.0.4, ignore@^5.1.1, ignore@^5.1.2, ignore@^5.2.0, ignore@^5.2.4: version "5.3.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== @@ -8072,7 +9167,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.3.0: +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -8116,7 +9211,7 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@^1.3.2, ini@^1.3.8: +ini@^1.3.2, ini@^1.3.5, ini@^1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -8196,6 +9291,15 @@ inquirer@^8.2.4: through "^2.3.6" wrap-ansi "^6.0.1" +internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== + dependencies: + get-intrinsic "^1.2.2" + hasown "^2.0.0" + side-channel "^1.0.4" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -8213,11 +9317,6 @@ invariant@*, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -inversify@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/inversify/-/inversify-5.1.1.tgz#6fbd668c591337404e005a1946bfe0d802c08730" - integrity sha512-j8grHGDzv1v+8T1sAQ+3boTCntFPfvxLCkNcxB1J8qA0lUN+fAlSyYd+RXKvaPRL4AGyPxViutBEJHNXOyUdFQ== - invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -8233,6 +9332,13 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== +is-accessor-descriptor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz#3223b10628354644b86260db29b3e693f5ceedd4" + integrity sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA== + dependencies: + hasown "^2.0.0" + is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -8241,6 +9347,15 @@ is-arguments@^1.1.1: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -8251,6 +9366,13 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -8258,11 +9380,31 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-buffer@~1.1.6: +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5, is-buffer@~1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-ci@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" @@ -8270,20 +9412,50 @@ is-ci@3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.13.0, is-core-module@^2.5.0, is-core-module@^2.8.1: +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.11.0, is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1: version "2.13.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: hasown "^2.0.0" -is-date-object@^1.0.5: +is-data-descriptor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz#2109164426166d32ea38c405c1e0945d9e6a4eeb" + integrity sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw== + dependencies: + hasown "^2.0.0" + +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" +is-descriptor@^0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.7.tgz#2727eb61fd789dcd5bdf0ed4569f551d2fe3be33" + integrity sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg== + dependencies: + is-accessor-descriptor "^1.0.1" + is-data-descriptor "^1.0.1" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.3.tgz#92d27cb3cd311c4977a4db47df457234a13cb306" + integrity sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw== + dependencies: + is-accessor-descriptor "^1.0.1" + is-data-descriptor "^1.0.1" + is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" @@ -8294,6 +9466,18 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -8321,7 +9505,7 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -8343,6 +9527,25 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -8358,7 +9561,7 @@ is-path-cwd@^2.2.0: resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== -is-path-inside@^3.0.2: +is-path-inside@^3.0.2, is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -8373,7 +9576,7 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-object@^2.0.4: +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -8398,6 +9601,13 @@ is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-ssh@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" @@ -8420,6 +9630,20 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + is-text-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" @@ -8432,6 +9656,13 @@ is-there@^4.3.3: resolved "https://registry.yarnpkg.com/is-there/-/is-there-4.5.1.tgz#ea292e7fad3fc4d70763fe0af40a286c9f5e1e2e" integrity sha512-vIZ7HTXAoRoIwYSsTnxb0sg9L6rth+JOulNcavsbskQkCIWoSM2cjFOWZs4wGziGZER+Xgs/HXiCQZgiL8ppxQ== +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -8442,6 +9673,18 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" @@ -8454,16 +9697,16 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== - isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -8476,16 +9719,34 @@ isobject@^2.0.0: dependencies: isarray "1.0.0" -isobject@^3.0.1: +isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== +istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" @@ -8705,6 +9966,25 @@ jest-get-type@^29.6.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== +jest-haste-map@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" + integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== + dependencies: + "@jest/types" "^24.9.0" + anymatch "^2.0.0" + fb-watchman "^2.0.0" + graceful-fs "^4.1.15" + invariant "^2.2.4" + jest-serializer "^24.9.0" + jest-util "^24.9.0" + jest-worker "^24.9.0" + micromatch "^3.1.10" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" + jest-haste-map@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" @@ -8742,6 +10022,20 @@ jest-matcher-utils@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + jest-message-util@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" @@ -8757,6 +10051,13 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" +jest-mock@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" + integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== + dependencies: + "@jest/types" "^24.9.0" + jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -8771,6 +10072,11 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== +jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + jest-regex-util@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" @@ -8859,6 +10165,11 @@ jest-runtime@^29.7.0: slash "^3.0.0" strip-bom "^4.0.0" +jest-serializer@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" + integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== + jest-serializer@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" @@ -8893,6 +10204,24 @@ jest-snapshot@^29.7.0: pretty-format "^29.7.0" semver "^7.5.3" +jest-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" + integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== + dependencies: + "@jest/console" "^24.9.0" + "@jest/fake-timers" "^24.9.0" + "@jest/source-map" "^24.9.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + callsites "^3.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.15" + is-ci "^2.0.0" + mkdirp "^0.5.1" + slash "^2.0.0" + source-map "^0.6.0" + jest-util@^27.2.0: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" @@ -8955,6 +10284,14 @@ jest-watcher@^29.7.0: jest-util "^29.7.0" string-length "^4.0.1" +jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + jest-worker@^27.2.0, jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -9067,6 +10404,11 @@ jscodeshift@^0.14.0: temp "^0.8.4" write-file-atomic "^2.3.0" +jsdoc-type-pratt-parser@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" + integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== + jsdom@^20.0.0: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" @@ -9109,6 +10451,11 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-loader@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" @@ -9134,6 +10481,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" @@ -9144,7 +10496,7 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== -json5@^1.0.1: +json5@^1.0.1, json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -9204,6 +10556,27 @@ keyboard-key@^1.0.4: resolved "https://registry.yarnpkg.com/keyboard-key/-/keyboard-key-1.1.0.tgz#6f2e8e37fa11475bb1f1d65d5174f1b35653f5b7" integrity sha512-qkBzPTi3rlAKvX7k0/ub44sqOfXeLc/jcnGGmj5c7BJpU8eDrEVPyhCvNYAaoubbsLm9uGWwQJO1ytQK1a9/dQ== +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + kind-of@^6.0.2, kind-of@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -9319,6 +10692,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + libnpmaccess@7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-7.0.2.tgz#7f056c8c933dd9c8ba771fa6493556b53c5aac52" @@ -9470,6 +10851,11 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.throttle@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" @@ -9651,6 +11037,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + map-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -9661,6 +11052,13 @@ map-obj@^4.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + marked@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/marked/-/marked-1.0.0.tgz#d35784245a04871e5988a491e28867362e941693" @@ -10619,6 +12017,25 @@ metro@0.76.8: ws "^7.5.1" yargs "^17.6.2" +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + micromatch@^4.0.0, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -10673,7 +12090,7 @@ minimatch@3.0.5: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -10703,7 +12120,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -10807,6 +12224,14 @@ minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -10870,11 +12295,33 @@ mute-stream@~1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== +nan@^2.12.1: + version "2.18.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" + integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== + nanoid@^3.3.6: version "3.3.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + nanospinner@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/nanospinner/-/nanospinner-1.1.0.tgz#d17ff621cb1784b0a206b400da88a0ef6db39b97" @@ -11091,6 +12538,13 @@ normalize-path@3, normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + npm-bundled@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1" @@ -11317,6 +12771,20 @@ object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + object-is@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" @@ -11330,6 +12798,69 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.getownpropertydescriptors@^2.1.6: + version "2.1.7" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz#7a466a356cd7da4ba8b9e94ff6d35c3eeab5d56a" + integrity sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g== + dependencies: + array.prototype.reduce "^1.0.6" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + safe-array-concat "^1.0.0" + +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -11396,6 +12927,18 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" @@ -11644,6 +13187,11 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -11757,7 +13305,7 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== -pirates@^4.0.4, pirates@^4.0.5: +pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.5: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== @@ -11797,6 +13345,11 @@ popper.js@^1.14.4: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + postcss-selector-parser@^6.0.10: version "6.0.13" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" @@ -11819,6 +13372,11 @@ pouchdb-collections@^1.0.1: resolved "https://registry.yarnpkg.com/pouchdb-collections/-/pouchdb-collections-1.0.1.tgz#fe63a17da977611abef7cb8026cb1a9553fd8359" integrity sha512-31db6JRg4+4D5Yzc2nqsRqsA2oOkZS8DpFav3jf/qVNBxusKa2ClkEIZ2bJNpaDbMfWtnuSq59p6Bn+CipPMdg== +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prettier@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.0.tgz#c6d16474a5f764ea1a4a373c593b779697744d5e" @@ -12296,6 +13854,14 @@ read-pkg-up@^3.0.0: find-up "^2.0.0" read-pkg "^3.0.0" +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA== + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -12394,6 +13960,13 @@ realistic-structured-clone@^3.0.0: typeson "^6.1.0" typeson-registry "^1.0.0-alpha.20" +realpath-native@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== + dependencies: + util.promisify "^1.0.0" + recast@^0.21.0: version "0.21.5" resolved "https://registry.yarnpkg.com/recast/-/recast-0.21.5.tgz#e8cd22bb51bcd6130e54f87955d33a2b2e57b495" @@ -12426,11 +13999,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -reflect-metadata@^0.1.12: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - regenerate-unicode-properties@^10.1.0: version "10.1.1" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" @@ -12460,6 +14028,14 @@ regenerator-transform@^0.15.2: dependencies: "@babel/runtime" "^7.8.4" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" @@ -12488,6 +14064,21 @@ regjsparser@^0.9.1: dependencies: jsesc "~0.5.0" +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -12535,6 +14126,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -12545,7 +14141,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -12570,6 +14166,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -12678,6 +14279,11 @@ rollup@^0.67.4: "@types/estree" "0.0.39" "@types/node" "*" +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -12702,6 +14308,16 @@ rxjs@^7.5.5, rxjs@^7.8.1: dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.0, safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -12712,6 +14328,22 @@ safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + safe-stable-stringify@^2.3.1: version "2.4.3" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" @@ -12722,6 +14354,21 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + sax@>=0.6.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" @@ -12791,7 +14438,7 @@ semantic-ui-react@^0.88.2: react-popper "^1.3.4" shallowequal "^1.1.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -12887,6 +14534,16 @@ set-function-name@^2.0.0: functions-have-names "^1.2.3" has-property-descriptors "^1.0.0" +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -12942,6 +14599,15 @@ shelljs@^0.8.4: interpret "^1.0.0" rechoir "^0.6.2" +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@3.0.7, signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -13029,6 +14695,36 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + socks-proxy-agent@^6.0.0: version "6.2.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" @@ -13140,7 +14836,7 @@ spdx-exceptions@^2.1.0: resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== -spdx-expression-parse@^3.0.0: +spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== @@ -13153,6 +14849,13 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + split2@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -13209,6 +14912,13 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg== +stack-utils@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" + integrity sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ== + dependencies: + escape-string-regexp "^2.0.0" + stack-utils@^2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" @@ -13228,6 +14938,14 @@ stacktrace-parser@^0.1.10, stacktrace-parser@^0.1.3: dependencies: type-fest "^0.7.1" +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -13299,6 +15017,33 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -13423,6 +15168,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -13555,6 +15307,16 @@ terser@^5.15.0, terser@^5.16.8: commander "^2.20.0" source-map-support "~0.5.20" +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -13574,6 +15336,11 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -13628,6 +15395,21 @@ to-fast-properties@^2.0.0: resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -13635,6 +15417,16 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -13689,6 +15481,11 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + ts-jest@^29.1.1: version "29.1.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" @@ -13714,6 +15511,28 @@ ts-loader@^9.4.3: semver "^7.3.4" source-map "^0.7.4" +ts-patch@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/ts-patch/-/ts-patch-3.0.2.tgz#cbdf88e4dfb596e4dab8f2c8269361d33270a0ba" + integrity sha512-iTg8euqiNsNM1VDfOsVIsP0bM4kAVXU38n7TGQSkky7YQX/syh6sDPIRkvSS0HjT8ZOr0pq1h+5Le6jdB3hiJQ== + dependencies: + chalk "^4.1.2" + global-prefix "^3.0.0" + minimist "^1.2.8" + resolve "^1.22.2" + semver "^7.3.8" + strip-ansi "^6.0.1" + +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tsconfig-paths@^4.1.2: version "4.2.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" @@ -13728,90 +15547,18 @@ tsconfig-paths@^4.1.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" - integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== - -tslib@^1.11.1, tslib@^1.7.1, tslib@^1.8.0, tslib@^1.8.1: +tslib@^1.11.1, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslint-config-airbnb@^5.8.0: - version "5.11.2" - resolved "https://registry.yarnpkg.com/tslint-config-airbnb/-/tslint-config-airbnb-5.11.2.tgz#2f3d239fa3923be8e7a4372217a7ed552671528f" - integrity sha512-mUpHPTeeCFx8XARGG/kzYP4dPSOgoCqNiYbGHh09qTH8q+Y1ghsOgaeZKYYQT7IyxMos523z/QBaiv2zKNBcow== - dependencies: - tslint-consistent-codestyle "^1.14.1" - tslint-eslint-rules "^5.4.0" - tslint-microsoft-contrib "~5.2.1" - -tslint-consistent-codestyle@^1.14.1: - version "1.16.0" - resolved "https://registry.yarnpkg.com/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.16.0.tgz#52348ea899a7e025b37cc6545751c6a566a19077" - integrity sha512-ebR/xHyMEuU36hGNOgCfjGBNYxBPixf0yU1Yoo6s3BrpBRFccjPOmIVaVvQsWAUAMdmfzHOCihVkcaMfimqvHw== - dependencies: - "@fimbul/bifrost" "^0.21.0" - tslib "^1.7.1" - tsutils "^2.29.0" - -tslint-eslint-rules@^5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5" - integrity sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w== - dependencies: - doctrine "0.7.2" - tslib "1.9.0" - tsutils "^3.0.0" - -tslint-microsoft-contrib@~5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz#a6286839f800e2591d041ea2800c77487844ad81" - integrity sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA== - dependencies: - tsutils "^2.27.2 <2.29.0" - -tslint@^5.7.0: - version "5.20.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" - integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.1" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.8.0" - tsutils "^2.29.0" - -tsutils@3, tsutils@^3.0.0, tsutils@^3.5.0: +tsutils@3: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" -"tsutils@^2.27.2 <2.29.0": - version "2.28.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.28.0.tgz#6bd71e160828f9d019b6f4e844742228f85169a1" - integrity sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA== - dependencies: - tslib "^1.8.1" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - tuf-js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" @@ -13821,6 +15568,13 @@ tuf-js@^1.1.7: debug "^4.3.4" make-fetch-happen "^11.1.1" +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-coverage-core@^2.17.2: version "2.26.3" resolved "https://registry.yarnpkg.com/type-coverage-core/-/type-coverage-core-2.26.3.tgz#47e2c8225f582d1ca9551c2bace20836b295c944" @@ -13847,6 +15601,11 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -13872,6 +15631,45 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typed-styles@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" @@ -13920,30 +15718,27 @@ typescript-coverage-report@^0.6.4: semantic-ui-react "^0.88.2" type-coverage-core "^2.17.2" +typescript-transform-paths@^3.4.6: + version "3.4.6" + resolved "https://registry.yarnpkg.com/typescript-transform-paths/-/typescript-transform-paths-3.4.6.tgz#28e6b24eb17a34116484a4b7af7323b8bb756db6" + integrity sha512-qdgpCk9oRHkIBhznxaHAapCFapJt5e4FbFik7Y4qdqtp6VyC3smAIPoDEIkjZ2eiF7x5+QxUPYNwJAtw0thsTw== + dependencies: + minimatch "^3.0.4" + typescript@4.2.x: version "4.2.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== -typescript@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5" - integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw== - -typescript@5.1.6: - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== - -"typescript@>=3 < 6", typescript@^5.0.2: +"typescript@>=3 < 6": version "5.3.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== -typescript@^4.3.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@~5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== typeson-registry@^1.0.0-alpha.20: version "1.0.0-alpha.39" @@ -13977,6 +15772,16 @@ ulid@^2.3.0: resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f" integrity sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -14005,6 +15810,16 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -14079,6 +15894,14 @@ unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + untildify@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.3.tgz#1e7b42b140bcfd922b22e70ca1265bfe3634c7c9" @@ -14137,11 +15960,29 @@ use-sync-external-store@^1.0.0: resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util.promisify@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.2.tgz#02b3dbadbb80071eee4c43aed58747afdfc516db" + integrity sha512-PBdZ03m1kBnQ5cjjO0ZvJMJS+QsbyIcFwi4hY4U76OQsCO9JrOYjbCFgIF76ccFg9xnJo7ZHPkqyj1GqmdS7MA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + for-each "^0.3.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + object.getownpropertydescriptors "^2.1.6" + safe-array-concat "^1.0.0" + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -14235,7 +16076,7 @@ walk-up-path@^3.0.1: resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-3.0.1.tgz#c8d78d5375b4966c717eb17ada73dbd41490e886" integrity sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA== -walker@^1.0.7, walker@^1.0.8: +walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -14422,6 +16263,17 @@ whatwg-url@^8.4.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" @@ -14432,7 +16284,18 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which@^1.2.9: +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -14566,6 +16429,15 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +write-file-atomic@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" + integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + write-file-atomic@5.0.1, write-file-atomic@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7"