From 11053b0702b1e694fb4638d6ee918800abbf582b Mon Sep 17 00:00:00 2001 From: Jim Eagan <84857865+hibler13@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:21:09 -0800 Subject: [PATCH 01/91] Minor edits to Vite + React quickstart (#6926) * Updates to Vite + React quickstart * Update src/fragments/gen2/quickstart/build-a-backend.mdx Co-authored-by: Kevin Old * Update build-a-backend.mdx * Update build-a-backend.mdx * Update build-a-backend.mdx --------- Co-authored-by: Kevin Old --- .../gen2/quickstart/build-a-backend.mdx | 16 +++++++------- .../start/quickstart/vite-react-app/index.mdx | 22 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/fragments/gen2/quickstart/build-a-backend.mdx b/src/fragments/gen2/quickstart/build-a-backend.mdx index ac76340dcbd..86edba24001 100644 --- a/src/fragments/gen2/quickstart/build-a-backend.mdx +++ b/src/fragments/gen2/quickstart/build-a-backend.mdx @@ -4,11 +4,11 @@ Next, we will add data and auth capabilities to the app. In Amplify (Gen 2), th ### Add data -`create-amplify` provides the scaffolding of a `/amplify/data/resource.ts` which is ready to deploy. +`create-amplify` provides the scaffolding of a `amplify/data/resource.ts` file, which is ready to deploy. - + -If you open up the `/amplify/data/resource.ts` file in your text editor, you will see some generated code already there. +Open the `amplify/data/resource.ts` file in your text editor, and you will see a default data model generated for you. _Note: The steps defined in the code below do not require action to complete this quickstart. These notes are provided for every new app and will help you when you build your next app on your own._ @@ -108,10 +108,10 @@ const schema = a.schema({ Once you save your changes to the data model, they will be deployed in seconds to your cloud sandbox. -The `Todo` data model is defined with authorization rules to allow the person who creates the Todo instance (the owner), to perform all actions on the data they own. We are also allowing all page viewers, including unauthenticated users, to read data. +The `Todo` data model is defined with authorization rules to allow the person who creates the Todo instance (the owner) to perform all actions on the data they own. We are also allowing all page viewers, including unauthenticated users, to read data. -**Note:** These authorization rules can be modified using a chain of methods as defined by default. For example, we could remove the `.to(['read'])` and allow all visitors to perform all actions on data or add permissions for signed-in users or users who belong to user groups such as `Admin`. You can learn more about all options for authorization in the [customize your auth rules](/gen2/build-a-backend/data/customize-authz/) section of the docs. +**Note:** These authorization rules can be modified using a chain of methods as defined by default. For example, we could remove the `.to(['read'])` and allow all visitors to perform all actions on data or add permissions for signed-in users or users who belong to user groups such as `Admin`. You can learn more about all options for authorization in the [Customize your auth rules](/gen2/build-a-backend/data/customize-authz/) section of the docs. Step 2: Remove public access by deleting the `a.allow.public().to(['read'])` authorization rule. Your authorization rule will look like the code below: @@ -124,7 +124,7 @@ The `Todo` data model is defined with authorization rules to allow the person wh // ... ``` -Below the schema declaration, you will see the `defineData` function which receives our schema and authorization configuration as arguments. The default configuration is for an `apiKey` to enable public access. +Below the schema declaration, you will see the `defineData` function, which receives our schema and authorization configuration as arguments. The default configuration is for an `apiKey` to enable public access. Step 3: Update the `defaultAuthorizationMode` to `userPool` so that the default is to use user authentication. @@ -144,7 +144,7 @@ export const data = defineData({ Now let's work on our authentication configuration. Similar to the `data/resource.ts` we just worked on, the `auth/resource.ts` file has code to define our authentication configuration. In this case, we are setting the authentication method to log in with email. - + ```ts title="amplify/auth/resource.ts" import { defineAuth } from '@aws-amplify/backend'; @@ -210,7 +210,7 @@ export const auth = defineAuth({ ``` -The Data and Auth resource files are imported into the `amplify/backend.ts` file which serves as the entrypoint to the Amplify backend for all resources used in this app. It is shown below, but there are no modifications needed to complete this quickstart. +The Data and Auth resource files are imported into the `amplify/backend.ts` file which serves as the entry point to the Amplify backend for all resources used in this app. It is shown below, but there are no modifications needed to complete this quickstart. ```ts title="amplify/backend.ts" import { defineBackend } from '@aws-amplify/backend'; diff --git a/src/pages/gen2/start/quickstart/vite-react-app/index.mdx b/src/pages/gen2/start/quickstart/vite-react-app/index.mdx index c3044bca54b..eaae7223129 100644 --- a/src/pages/gen2/start/quickstart/vite-react-app/index.mdx +++ b/src/pages/gen2/start/quickstart/vite-react-app/index.mdx @@ -1,6 +1,6 @@ export const meta = { title: 'Vite + React App', - description: 'Get started with AWS Amplify (Gen 2) using React + Vite.' + description: 'Get started with AWS Amplify (Gen 2) using Vite + React.' }; export function getStaticProps(context) { @@ -11,7 +11,7 @@ export function getStaticProps(context) { }; } -This Quickstart guide will walk you through how to build a task list application with TypeScript, Vite, and React. If you are new to these technologies, we recommend you go through the [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html), [Vite](https://vitejs.dev/guide/), and official [React](https://react.dev/learn/tutorial-tic-tac-toe) tutorials first. +This quickstart guide will walk you through how to build a task-list app with TypeScript, Vite, and React. If you are new to these technologies, we recommend you go through the [TypeScript](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html), [Vite](https://vitejs.dev/guide/), and official [React](https://react.dev/learn/tutorial-tic-tac-toe) tutorials first. import prerequisites from 'src/fragments/gen2/quickstart/prerequisites.mdx'; @@ -103,7 +103,7 @@ First, install the Amplify UI component library: npm install @aws-amplify/ui-react ``` -Next, configure the Amplify libraries client-side so it can interact with backend services. +Next, configure the Amplify library client-side so it can interact with backend services. Open `src/main.tsx` and add the following code below the last import: @@ -114,7 +114,7 @@ import amplifyconfig from '../amplifyconfiguration.json'; Amplify.configure(amplifyconfig); ``` -In `src/App.tsx`, update with the following code which imports Amplify UI and wraps your App in the `withAuthenticator` function: +Update the `src/App.tsx` file with the following code to import Amplify UI and wrap your app in the `withAuthenticator` function: ```tsx title="src/App.tsx" import { withAuthenticator } from "@aws-amplify/ui-react"; @@ -131,7 +131,7 @@ function App() { export default withAuthenticator(App); ``` -Run your application with `npm run dev` and navigate to `http://localhost:5173`. You should now see the authenticator, which is already configured and ready for your first sign-up! Create a new user account, confirm the account through email, and then sign in. +Run your app with `npm run dev` and navigate to `http://localhost:5173`. You should now see the authenticator, which is already configured and ready for your first sign-up! Create a new user account, confirm the account through email, and then sign in. ### View list of to-do items @@ -223,7 +223,7 @@ Let's update the return statement of the `components/TodoList` component to have } ``` -Create a couple of to-dos, then refresh the page to see them. You can also subscribe to new to-dos in your `useEffect` to have them live reload on the page. +Create a couple of to-dos, then refresh the page to see them. You can also subscribe to new to-dos in your `useEffect` to have them live-reload on the page. ```js useEffect(() => { @@ -253,15 +253,15 @@ If you already have your project remotely hosted in a Git repository, you can sk ### Connect branch in the Amplify console 1. To get started with Gen 2, log in to the [AWS console](https://console.aws.amazon.com/console/home?#amplify) and navigate to your preferred AWS Region. (The Amplify console is available in [19 AWS Regions](https://docs.aws.amazon.com/general/latest/gr/amplify.html#amplify_region)). -2. From the _Public Preview_ banner, choose _Try Amplify Gen 2_. +2. From the **Public Preview** banner, choose **Try Amplify Gen 2**. -![Amplify Gen 2 console showing the "Public Preview" banner with "Try Amplify Gen 2" button](/images/gen2/getting-started/console1.png) +![Amplify Gen 2 console showing the "Public Preview" banner with "Try Amplify Gen 2" button.](/images/gen2/getting-started/console1.png) -3. In the Git provider screen, choose _Option 2: Start with an existing app_. Connect the repository you just deployed and pick a branch. +3. In the Git provider screen, choose **Option 2: Start with an existing app**. Connect the repository you just deployed and pick a branch. -If you have an existing Gen 2 application, you can modify the build image by navigating to _Hosting_ -> _Environment Variables_ and create a new environment variable `_CUSTOM_IMAGE` with the value `amplify:al2023`, then redeploy the build. +If you have an existing Gen-2 app, you can modify the build image by navigating to **Hosting > Environment variables** and creating a new environment variable `_CUSTOM_IMAGE` with the value `amplify:al2023`, then redeploy the build. -4. Review all of your settings to ensure everything is set up correctly. Choose _Save and deploy_ to deploy your web app. +4. Review all of your settings to ensure everything is set up correctly. Choose **Save and deploy** to deploy your web app. ### Manage fullstack branch From c54850cf116c66b9db8a852e8f21e7287d16b58a Mon Sep 17 00:00:00 2001 From: Jim Blanchard Date: Tue, 20 Feb 2024 10:00:33 -0600 Subject: [PATCH 02/91] fix: Fix incorrect auth import path in migration guide. (#6934) --- .../build-a-backend/auth/auth-migration-guide/index.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx index b900b2669f2..aa9b38fb58c 100644 --- a/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx @@ -3815,7 +3815,10 @@ This API has been deprecated: existing use cases can be migrated by using a comb ```js - import { getCurrentUser, fetchUserAttributes } from 'aws-amplify'; + import { + getCurrentUser, + fetchUserAttributes + } from 'aws-amplify/auth'; const getCurrentUserInfo = async () => { const { From d9e1afabd24d50c393fa7a36df178b50b2f9e3b9 Mon Sep 17 00:00:00 2001 From: Jim Blanchard Date: Tue, 20 Feb 2024 15:21:57 -0600 Subject: [PATCH 03/91] fix: Update incorrect `updateMFAPreference` parameter in JS v6 migration guide. (#6935) --- .../build-a-backend/auth/auth-migration-guide/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx index aa9b38fb58c..16680a19d67 100644 --- a/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/auth-migration-guide/index.mdx @@ -1840,8 +1840,8 @@ In v6, `Auth.setPreferredMFA` has been replaced by `updateMFAPreference`, which ```js import { updateMFAPreference } from 'aws-amplify/auth'; - const handleSetPreferredMFA = async ({ user }) => { - await updateMFAPreference({ mfa: 'PREFERRED' }); + const handleSetPreferredMFA = async () => { + await updateMFAPreference({ totp: 'PREFERRED' }); } ``` From abea35b0b54d97e8970dadb9cf37e6d46a05879a Mon Sep 17 00:00:00 2001 From: Ankit Shah <22114629+ankpshah@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:47:40 -0800 Subject: [PATCH 04/91] Amplify Android Release 2.14.11 (#6933) --- src/constants/versions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/versions.ts b/src/constants/versions.ts index 9d863a1f52a..89d8b2f8e2b 100644 --- a/src/constants/versions.ts +++ b/src/constants/versions.ts @@ -1,5 +1,5 @@ export const versions = { - ANDROID_VERSION: '2.14.10', + ANDROID_VERSION: '2.14.11', ANDROID_DEVPREVIEW: '1.36.5-dev-preview.0', ANDROID_V1_VERSION: '1.38.8', ANDROID_V1_GEO_VERSION: '1.0.1', From f4fe23e206565f5af18f8f2214bc0f8e9e70916d Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Wed, 21 Feb 2024 09:26:51 -0800 Subject: [PATCH 05/91] chore: refactor .layout-header into separate component (#6826) * platform typed as optional * refactor init * remove commented code * remove angry useEffect * fix typing * added layoutcontext so mobile menu closes on navigation * move import --------- Co-authored-by: katiegoines --- src/components/Breadcrumbs/index.tsx | 7 +- src/components/Layout/Layout.tsx | 138 ++-------------------- src/components/Layout/LayoutHeader.tsx | 157 +++++++++++++++++++++++++ src/components/Layout/index.ts | 1 + 4 files changed, 174 insertions(+), 129 deletions(-) create mode 100644 src/components/Layout/LayoutHeader.tsx diff --git a/src/components/Breadcrumbs/index.tsx b/src/components/Breadcrumbs/index.tsx index a87bab2be84..eb1995ece9c 100644 --- a/src/components/Breadcrumbs/index.tsx +++ b/src/components/Breadcrumbs/index.tsx @@ -12,7 +12,7 @@ type BreadcrumbItem = { type Props = { route: string; - platform: string; + platform?: string; }; const overrides = { @@ -39,7 +39,7 @@ const overrides = { function generateBreadcrumbs( route: string, - platform: string + platform?: string ): BreadcrumbItem[] { const breadcrumbs: BreadcrumbItem[] = []; @@ -58,9 +58,10 @@ function generateBreadcrumbs( href['query'] = { platform }; } let label = directoryEntry ? directoryEntry.title : url; + const override = overrides[url] ? overrides[url] - : overrides[url.replace('[platform]', platform)]; + : overrides[url.replace('[platform]', platform!)]; if (override) { label = override; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index ea4346e35bb..41b5b57d891 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,17 +1,14 @@ -import { useState, useEffect, useRef, ReactElement } from 'react'; +import { useState, useEffect, ReactElement } from 'react'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { - Button, ColorMode, Flex, Heading, IconsProvider, ThemeProvider, - View, - VisuallyHidden + View } from '@aws-amplify/ui-react'; -import classNames from 'classnames'; import { defaultIcons } from '@/themes/defaultIcons'; import { defaultTheme } from '@/themes/defaultTheme'; import { gen2Theme } from '@/themes/gen2Theme'; @@ -23,28 +20,15 @@ import { PLATFORM_DISPLAY_NAMES, Platform } from '@/data/platforms'; -import { - ALGOLIA_API_KEY, - ALGOLIA_INDEX_NAME, - ALGOLIA_APP_ID -} from '../../constants/algolia'; import { GEN2BANNER_URLS } from '@/data/gen2Banner-urls'; import { SpaceShip } from '@/components/SpaceShip'; -import { IconMenu, IconDoubleChevron } from '@/components/Icons'; import { LEFT_NAV_LINKS, RIGHT_NAV_LINKS } from '@/utils/globalnav'; -import { Menu } from '@/components/Menu'; -import { LayoutProvider } from '@/components/Layout'; +import { LayoutProvider, LayoutHeader } from '@/components/Layout'; import { TableOfContents } from '@/components/TableOfContents'; import type { HeadingInterface } from '@/components/TableOfContents/TableOfContents'; -import { PlatformNavigator } from '@/components/PlatformNavigator'; -import flatDirectory from '@/directory/flatDirectory.json'; import { Breadcrumbs } from '@/components/Breadcrumbs'; import { debounce } from '@/utils/debounce'; -import { DocSearch } from '@docsearch/react'; import '@docsearch/css'; -import { PageLastUpdated } from '../PageLastUpdated'; -import Feedback from '../Feedback'; -import RepoActions from '../Menu/RepoActions'; import { Banner } from '@/components/Banner'; import { usePathWithoutHash } from '@/utils/usePathWithoutHash'; import { @@ -78,8 +62,6 @@ export const Layout = ({ const [menuOpen, toggleMenuOpen] = useState(false); const [colorMode, setColorMode] = useState('system'); const [tocHeadings, setTocHeadings] = useState([]); - const menuButtonRef = useRef(null); - const sidebarMenuButtonRef = useRef(null); const mainId = 'pageMain'; const showTOC = hasTOC && tocHeadings.length > 0; const router = useRouter(); @@ -92,7 +74,6 @@ export const Layout = ({ let currentPlatform = isGen2 ? undefined : DEFAULT_PLATFORM; const isContributor = asPathWithNoHash.split('/')[1] === 'contribute'; const currentGlobalNavMenuItem = isContributor ? 'Contribute' : 'Docs'; - const isPrev = asPathWithNoHash.split('/')[2] === 'prev'; const handleColorModeChange = (mode: ColorMode) => { setColorMode(mode); @@ -125,8 +106,8 @@ export const Layout = ({ currentPlatform = platform ? platform : PLATFORMS.includes(asPathPlatform) - ? asPathPlatform - : DEFAULT_PLATFORM; + ? asPathPlatform + : DEFAULT_PLATFORM; } const title = [ @@ -139,18 +120,6 @@ export const Layout = ({ const description = `${pageDescription} AWS Amplify Documentation`; - const handleMenuToggle = () => { - if (!menuOpen) { - toggleMenuOpen(true); - // For keyboard navigators, move focus to the close menu button in the nav - setTimeout(() => sidebarMenuButtonRef?.current?.focus(), 0); - } else { - toggleMenuOpen(false); - // For keyboard navigators, move focus back to menu button in header - menuButtonRef?.current?.focus(); - } - }; - const handleScroll = debounce((e) => { const bodyScroll = e.target.documentElement.scrollTop; if (bodyScroll > 20) { @@ -258,96 +227,13 @@ export const Layout = ({ isGen2={isGen2} mainId={mainId} /> - - - - - - - - - - - - toggleMenuOpen(false)} - > - - - {isGen2 ? null : ( -
- -
- )} - -
- -
- - -
- {showLastUpdatedDate && ( - - )} -
-
-
-
+ { + const { menuOpen, toggleMenuOpen } = useContext(LayoutContext); + const menuButtonRef = useRef(null); + const sidebarMenuButtonRef = useRef(null); + const showTOC = hasTOC && tocHeadings.length > 0; + const router = useRouter(); + const asPathWithNoHash = usePathWithoutHash(); + const isGen2 = asPathWithNoHash.split('/')[1] === 'gen2'; + let currentPlatform = isGen2 ? undefined : DEFAULT_PLATFORM; + const isPrev = asPathWithNoHash.split('/')[2] === 'prev'; + + if (!isGen2) { + // [platform] will always be the very first subpath right? + // when using `router.asPath` it returns a string that starts with a '/' + // To get the "platform" the client was trying to visit, we have to get the string at index 1 + // Doing this because when visiting a 404 page, there is no `router.query.platform`, so we have + // to check where the user was trying to visit from + const asPathPlatform = asPathWithNoHash.split('/')[1] as Platform; + + currentPlatform = platform + ? platform + : PLATFORMS.includes(asPathPlatform) + ? asPathPlatform + : DEFAULT_PLATFORM; + } + + const handleMenuToggle = () => { + if (!menuOpen) { + toggleMenuOpen(true); + // For keyboard navigators, move focus to the close menu button in the nav + setTimeout(() => sidebarMenuButtonRef?.current?.focus(), 0); + } else { + toggleMenuOpen(false); + // For keyboard navigators, move focus back to menu button in header + menuButtonRef?.current?.focus(); + } + }; + + return ( + + + + + + + + + + + + toggleMenuOpen(false)} + > + + + {isGen2 ? null : ( +
+ +
+ )} + +
+ +
+ + +
+ {showLastUpdatedDate && ( + + )} +
+
+
+
+ ); +}; diff --git a/src/components/Layout/index.ts b/src/components/Layout/index.ts index dcc041c8ab2..ed79ab48baa 100644 --- a/src/components/Layout/index.ts +++ b/src/components/Layout/index.ts @@ -1,2 +1,3 @@ export { Layout } from './Layout'; +export { LayoutHeader } from './LayoutHeader'; export { LayoutContext, LayoutProvider } from './LayoutProvider'; From 20df39945111c2f04fcf37fd129d144935101ea4 Mon Sep 17 00:00:00 2001 From: Dan Kiuna Date: Wed, 21 Feb 2024 16:02:18 -0600 Subject: [PATCH 06/91] fix: Nesting of auth cli templates (#6932) --- .../tools/cli/usage/lambda-triggers/index.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/[platform]/tools/cli/usage/lambda-triggers/index.mdx b/src/pages/[platform]/tools/cli/usage/lambda-triggers/index.mdx index a01b10f8ae5..2b58a90d3f7 100644 --- a/src/pages/[platform]/tools/cli/usage/lambda-triggers/index.mdx +++ b/src/pages/[platform]/tools/cli/usage/lambda-triggers/index.mdx @@ -120,7 +120,7 @@ import trigger from '/src/fragments/cli/lambda_triggers_callout.mdx'; The CLI Auth workflow provides the following Lambda trigger templates: -### Custom Auth Challenge with Google reCaptcha +#### Custom Auth Challenge with Google reCaptcha Captchas allow front end applications to guard against bots or other unwanted page interactions by presenting a challenge that is designed to require human intervention. The Google reCaptcha service is a popular implementation of captcha. @@ -277,23 +277,23 @@ export class AppComponent {
-### Basic Scaffolding for a Custom Auth Challenge +#### Basic Scaffolding for a Custom Auth Challenge This template will configure three triggers: [CreateAuthChallenge](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-create-auth-challenge.html), [DefineAuthChallenge](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html), and [VerifyAuthChallengeResponse](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-verify-auth-challenge-response.html). It will not, however, provide a fully-formed custom authentication flow. Instead, it will create a 'hello world' custom auth flow skeleton that you can manually edit. The intent of this template is to give you a starting place for building out your own custom auth flow. -### Add User to Group +#### Add User to Group This trigger allows you to define a Cognito group to which a user will be added upon registration. The trigger will check for the existence of the group in your User Pool, and will create the group if it is not present. -### Email Domain Filtering (deny list) and Email Domain Filtering (allow list) +#### Email Domain Filtering (deny list) and Email Domain Filtering (allow list) These two templates allow you to define email domains which are allowed or disallowed (respectively). They can be used in tandem or individually. -### Override ID Token Claims +#### Override ID Token Claims This template uses the Pre Token Generation trigger and allows you to add, override or remove claims from the [ID token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html#amazon-cognito-user-pools-using-the-id-token) that is returned by Cognito. From d29e872ff9d115960457325141c02943afc2c7cf Mon Sep 17 00:00:00 2001 From: "Kihara, Takuya" Date: Thu, 22 Feb 2024 08:50:04 +0900 Subject: [PATCH 07/91] fix: change config variable name (#6923) --- src/pages/[platform]/build-ui/uibuilder/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/[platform]/build-ui/uibuilder/index.mdx b/src/pages/[platform]/build-ui/uibuilder/index.mdx index e90754d0fe4..290a87f7b2f 100644 --- a/src/pages/[platform]/build-ui/uibuilder/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/index.mdx @@ -1,5 +1,5 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; - + export const meta = { title: 'Figma-to-Code', description: 'Generate clean React code from Figma design files with Amplify Studio.', @@ -30,7 +30,7 @@ export function getStaticProps(context) { } }; } - + Amplify Studio offers an integration with Figma, allowing you to generate clean React code by importing your Figma design file. [Figma](https://figma.com/) is a browser-based UI and UX design application that is used to build high-fidelity designs. In the standard product development lifecycle, UI or UX designers build mockups that get implemented as code by developers. Amplify Studio automatically converts any [Figma component](https://help.figma.com/hc/en-us/articles/360038662654-Guide-to-Components-in-Figma) in your Figma file to a [React component](https://reactjs.org/docs/components-and-props.html) that is then usable in your app. @@ -83,7 +83,7 @@ import amplifyconfig from './amplifyconfiguration.json'; import "@aws-amplify/ui-react/styles.css"; import studioTheme from './ui-components/studioTheme'; -Amplify.configure(awsconfig); +Amplify.configure(amplifyconfig); ``` 3. In your application's entrypoint file (e.g. `src/index.js` for create-react-app or `src/main.jsx` for Vite), wrap the `` with the following: From 2aef692d14caa1de0bfc2e48d2ff771e52ad06e0 Mon Sep 17 00:00:00 2001 From: Tyler Roach Date: Thu, 22 Feb 2024 12:38:22 -0500 Subject: [PATCH 08/91] Guide to support Amplify v2 with AWS Android SDK (#6927) --- src/directory/directory.mjs | 3 + .../amplify-compatibility/index.mdx | 278 ++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 src/pages/[platform]/sdk/configuration/amplify-compatibility/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 52a5bcea396..ea06a211cb6 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -1901,6 +1901,9 @@ export const directory = { children: [ { path: 'src/pages/[platform]/sdk/configuration/setup-options/index.mdx' + }, + { + path: 'src/pages/[platform]/sdk/configuration/amplify-compatibility/index.mdx' } ] }, diff --git a/src/pages/[platform]/sdk/configuration/amplify-compatibility/index.mdx b/src/pages/[platform]/sdk/configuration/amplify-compatibility/index.mdx new file mode 100644 index 00000000000..60af44a8e19 --- /dev/null +++ b/src/pages/[platform]/sdk/configuration/amplify-compatibility/index.mdx @@ -0,0 +1,278 @@ +import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; + +export const meta = { + title: 'Amplify v2 Compatibility', + description: + 'Learn how to use the AWS SDK with Amplify v2.', + platforms: ['android'] +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + return { + props: { + platform: context.params.platform, + meta + } + }; +} + + + +The AWS Mobile Client (com.amazonaws:aws-android-sdk-mobile-client) and Amplify Android v2 are not compatible with each other. Amplify v2 migrates the credentials from AWS Mobile Client into a different format, leaving AWS Mobile Client unable to read the credentials. If AWS Mobile Client is launched after this migration has taken place, the Amplify v2 credentials will also be cleared. + + +## Using Amplify V2 Auth with AWS Android SDK Plugin + +We recommend using Amplify v2 with the [AWS Kotlin SDK](https://aws.amazon.com/sdk-for-kotlin/), rather than the AWS Android SDK. In order to better support existing implementations, this guide demonstrates how to continue using AWS Android SDK plugins with Amplify v2. + +### Creating an AmplifyCredentialsProvider + +Many of the AWS Android SDK plugins accept a custom `AWSCredentialsProvider` implementation. You can implement your own `AWSCredentialsProvider` that uses Amplify Android v2 to provide credentials. + + + + +```java +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.BasicSessionCredentials; +import com.amplifyframework.auth.AWSTemporaryCredentials; +import com.amplifyframework.auth.cognito.AWSCognitoAuthSession; +import com.amplifyframework.auth.options.AuthFetchSessionOptions; +import com.amplifyframework.core.Amplify; + +import java.util.concurrent.CompletableFuture; + +class AmplifyCredentialsProvider implements AWSCredentialsProvider { + + @Override + public AWSCredentials getCredentials() { + CompletableFuture sdkCredentials = new CompletableFuture<>(); + + Amplify.Auth.fetchAuthSession((authSession) -> { + BasicSessionCredentials credentials = null; + if (authSession instanceof AWSCognitoAuthSession) { + AWSCognitoAuthSession cognitoAuthSession = (AWSCognitoAuthSession) authSession; + com.amplifyframework.auth.AWSCredentials awsCredentials = + cognitoAuthSession.getAwsCredentialsResult().getValue(); + if (awsCredentials instanceof AWSTemporaryCredentials) { + AWSTemporaryCredentials temporaryAwsCredentials = + (AWSTemporaryCredentials) awsCredentials; + credentials = new BasicSessionCredentials( + temporaryAwsCredentials.getAccessKeyId(), + temporaryAwsCredentials.getSecretAccessKey(), + temporaryAwsCredentials.getSessionToken() + ); + } + } + + if (credentials != null) { + sdkCredentials.complete(credentials); + } else { + sdkCredentials.completeExceptionally( + new RuntimeException("Failed to get credentials") + ); + } + }, (exception) -> sdkCredentials.completeExceptionally( + new RuntimeException("Failed to get credentials", exception) + )); + + return sdkCredentials.join(); + } + + @Override + public void refresh() { + CompletableFuture result = new CompletableFuture<>(); + Amplify.Auth.fetchAuthSession( + AuthFetchSessionOptions.builder().forceRefresh(true).build(), + // We do not need to capture value if refresh succeeds + (authSession) -> result.complete(null), + // We do not need to throw if refresh fails + (exception) -> result.complete(null) + ); + + result.join(); + } +} +``` + + + + +```kotlin +import com.amazonaws.auth.AWSCredentials +import com.amazonaws.auth.AWSCredentialsProvider +import com.amazonaws.auth.BasicSessionCredentials +import com.amplifyframework.auth.AWSTemporaryCredentials +import com.amplifyframework.auth.cognito.AWSCognitoAuthSession +import com.amplifyframework.auth.options.AuthFetchSessionOptions +import com.amplifyframework.core.Amplify +import java.lang.RuntimeException +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.runBlocking + +class AmplifyCredentialsProvider : AWSCredentialsProvider { + + override fun getCredentials(): AWSCredentials = runBlocking { + suspendCoroutine { continuation -> + Amplify.Auth.fetchAuthSession( + { authSession -> + val awsTemporaryCredentials = (authSession as? AWSCognitoAuthSession) + ?.awsCredentialsResult?.value as? AWSTemporaryCredentials + + val sdkCredentials = awsTemporaryCredentials?.let { + BasicSessionCredentials(it.accessKeyId, it.secretAccessKey, it.sessionToken) + } + + if (sdkCredentials != null) { + continuation.resume(sdkCredentials) + } else { + val authException = RuntimeException("Failed to get credentials") + continuation.resumeWithException(authException) + } + }, + { + continuation.resumeWithException( + RuntimeException("Failed to get credentials. See exception.", it) + ) + } + ) + } + } + + override fun refresh() = runBlocking { + suspendCoroutine { continuation -> + Amplify.Auth.fetchAuthSession( + AuthFetchSessionOptions.builder().forceRefresh(true).build(), + // We do not need to capture value if refresh succeeds + { continuation.resume(Unit) }, + // We do not need to throw if refresh fails + { continuation.resume(Unit) } + ) + } + } +} +``` + + + + +You can now use your `AmplifyCredentialsProvider` in any plugins that accept an `AWSCredentialsProvider`, instead of using `AWSMobileClient.getInstance()` as your AWSCredentialsProvider. + + +## Providing AWS Configuration Information + +Amplify v2 uses the `amplifyconfiguration.json` file where AWS Android SDK uses the `awsconfiguration.json` file. If you are using both Amplify v2 and AWS Android SDK in your project, it is important to ensure the resources are in sync. The Amplify CLI still generates and updates both of these file types, but any manual customizations should be applied to both files. + +For AWS Android SDK plugins that require configuration information, you can continue to use the `AWSConfiguration` class. + + + + +```java +AWSConfiguration awsConfiguration = new AWSConfiguration(context); +``` + + + + +```kotlin +val awsConfiguration = AWSConfiguration(context) +``` + + + + +## Example Usage of AWS Android SDK Plugins with Amplify v2 + +This is not an exhaustive list of supported plugins. Any plugins that accept an AWSCredentialsProvider and do not rely on AWS Mobile Client should work. + +### S3 Storage (com.amazonaws:aws-android-sdk-s3) + + + + +```java +AWSConfiguration awsConfiguration = new AWSConfiguration(context); +TransferUtility transferUtility = TransferUtility.builder() + .context(context) + .awsConfiguration(awsConfig) + .s3Client( + new AmazonS3Client( + new AmplifyCredentialsProvider(), + Region.getRegion(Regions.US_EAST_1) + ) + ) + .build(); +``` + + + + +```kotlin +val awsConfiguration = AWSConfiguration(context) +val transferUtility = TransferUtility.builder() + .context(context) + .awsConfiguration(awsConfiguration) + .s3Client( + AmazonS3Client( + AmplifyCredentialsProvider(), + Region.getRegion(Regions.US_EAST_1) + ) + ) + .build() +``` + + + + +### IoT (com.amazonaws:aws-android-sdk-iot) + + + + +```java +AWSIotClient client = new AWSIotClient(new AmplifyCredentialsProvider()); + +``` + + + + +```kotlin +val client = AWSIotClient(AmplifyCredentialsProvider()) +``` + + + + + + +### Android SDK Generated by API Gateway (aws-android-sdk-apigateway-core) + + + + +```java +ApiClientFactory clientFactory = new ApiClientFactory(); +clientFactory.credentialsProvider(new AmplifyCredentialsProvider()); +``` + + + + +```kotlin +val clientFactory = ApiClientFactory() +clientFactory.credentialsProvider(AmplifyCredentialsProvider()) +``` + + + + + From ea39d66824453bb1ba90fa82b4649a89018fa131 Mon Sep 17 00:00:00 2001 From: Tim Nguyen <54393192+timngyn@users.noreply.github.com> Date: Thu, 22 Feb 2024 12:34:13 -0800 Subject: [PATCH 09/91] Update puppeteer (#6949) --- package.json | 4 +- yarn.lock | 174 ++++++++++++++++++++++----------------------------- 2 files changed, 77 insertions(+), 101 deletions(-) diff --git a/package.json b/package.json index 23e85d5a6e0..4922a142c58 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "lint-staged": "^14.0.0", "next-bundle-analyzer": "^0.6.7", "prettier": "^3.0.3", - "puppeteer": "^20.8.2", + "puppeteer": "^22.2.0", "rehype": "^11.0.0", "rehype-img-size": "^1.0.1", "rehype-mdx-code-props": "^2.0.0", @@ -91,7 +91,7 @@ "follow-redirects": "^1.15.4", "ip": "2.0.1", "sharp": "0.32.6" - }, + }, "scripts": { "clean": "rm -rf node_modules yarn.lock", "refresh": "yarn clean && yarn", diff --git a/yarn.lock b/yarn.lock index f962b821a04..6aee437ac0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2029,18 +2029,19 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== -"@puppeteer/browsers@1.4.6": - version "1.4.6" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.4.6.tgz#1f70fd23d5d2ccce9d29b038e5039d7a1049ca77" - integrity sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ== +"@puppeteer/browsers@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-2.1.0.tgz#2683d3c908ecfc9af6b63111b5037679d3cebfd8" + integrity sha512-xloWvocjvryHdUjDam/ZuGMh7zn4Sn3ZAaV4Ah2e2EwEt90N3XphZlSsU3n0VDc1F7kggCjMuH0UuxfPQ5mD9w== dependencies: debug "4.3.4" extract-zip "2.0.1" progress "2.0.3" - proxy-agent "6.3.0" - tar-fs "3.0.4" + proxy-agent "6.4.0" + semver "7.6.0" + tar-fs "3.0.5" unbzip2-stream "1.4.3" - yargs "17.7.1" + yargs "17.7.2" "@radix-ui/number@1.0.0": version "1.0.0" @@ -4170,12 +4171,13 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chromium-bidi@0.4.16: - version "0.4.16" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.16.tgz#8a67bfdf6bb8804efc22765a82859d20724b46ab" - integrity sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA== +chromium-bidi@0.5.9: + version "0.5.9" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.5.9.tgz#f068bd10e8a3007bc842ae99f5038c8689bf58a5" + integrity sha512-wOTX3m2zuHX0zRX4h7Ol1DAGz0cqHzo2IrAPvOqBxdd4ZR32vxg4FKNjmBihi1oP9b1QGSBBG5VNUUXUCsxDfg== dependencies: - mitt "3.0.0" + mitt "3.0.1" + urlpattern-polyfill "10.0.0" ci-info@^2.0.0: version "2.0.0" @@ -4540,15 +4542,15 @@ cosmiconfig@8.0.0: parse-json "^5.0.0" path-type "^4.0.0" -cosmiconfig@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" - integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== +cosmiconfig@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== dependencies: - import-fresh "^3.2.1" + env-paths "^2.2.1" + import-fresh "^3.3.0" js-yaml "^4.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" + parse-json "^5.2.0" create-require@^1.1.0: version "1.1.1" @@ -4929,10 +4931,10 @@ devlop@^1.0.0, devlop@^1.1.0: dependencies: dequal "^2.0.0" -devtools-protocol@0.0.1147663: - version "0.0.1147663" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz#4ec5610b39a6250d1f87e6b9c7e16688ed0ac78e" - integrity sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ== +devtools-protocol@0.0.1249869: + version "0.0.1249869" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1249869.tgz#000c3cf1afc189a18db98135a50d4a8f95a47d29" + integrity sha512-Ctp4hInA0BEavlUoRy9mhGq0i+JSo/AwVyX2EFgZmV1kYB+Zq+EMBAn52QWu6FbRr10hRb6pBl420upbp4++vg== diff-sequences@^26.6.2: version "26.6.2" @@ -5092,6 +5094,11 @@ enquirer@^2.3.6: ansi-colors "^4.1.1" strip-ansi "^6.0.1" +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -6487,7 +6494,7 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3: +https-proxy-agent@^7.0.2, https-proxy-agent@^7.0.3: version "7.0.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== @@ -8870,10 +8877,10 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== -mitt@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd" - integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ== +mitt@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== mixin-deep@^1.2.0: version "1.3.2" @@ -9353,7 +9360,7 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^7.0.0, pac-proxy-agent@^7.0.1: +pac-proxy-agent@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== @@ -9419,7 +9426,7 @@ parse-imports@^1.1.0: es-module-lexer "^1.3.0" slashes "^3.0.12" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -9669,21 +9676,7 @@ property-information@^6.0.0: resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.4.1.tgz#de8b79a7415fd2107dfbe65758bb2cc9dfcf60ac" integrity sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w== -proxy-agent@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.0.tgz#72f7bb20eb06049db79f7f86c49342c34f9ba08d" - integrity sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og== - dependencies: - agent-base "^7.0.2" - debug "^4.3.4" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" - lru-cache "^7.14.1" - pac-proxy-agent "^7.0.0" - proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.1" - -proxy-agent@^6.3.0: +proxy-agent@6.4.0, proxy-agent@^6.3.0: version "6.4.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== @@ -9725,26 +9718,26 @@ punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -puppeteer-core@20.9.0: - version "20.9.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-20.9.0.tgz#6f4b420001b64419deab38d398a4d9cd071040e6" - integrity sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg== +puppeteer-core@22.2.0: + version "22.2.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-22.2.0.tgz#85bf2c492ba02e8051bb75dff3043d21d9945f21" + integrity sha512-rxLM860FP05CxCPAn6dwY0KnVhbnogsXu4XORb+2hb/va69v7R1VdJWLMGHd7EE5wfpT8oFZ7Q6NN85OhOtV9Q== dependencies: - "@puppeteer/browsers" "1.4.6" - chromium-bidi "0.4.16" + "@puppeteer/browsers" "2.1.0" + chromium-bidi "0.5.9" cross-fetch "4.0.0" debug "4.3.4" - devtools-protocol "0.0.1147663" - ws "8.13.0" + devtools-protocol "0.0.1249869" + ws "8.16.0" -puppeteer@^20.8.2: - version "20.9.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-20.9.0.tgz#7bfb9e37deab9728e13b02ea3fb499b5560c79a7" - integrity sha512-kAglT4VZ9fWEGg3oLc4/de+JcONuEJhlh3J6f5R1TLkrY/EHHIHxWXDOzXvaxQCtedmyVXBwg8M+P8YCO/wZjw== +puppeteer@^22.2.0: + version "22.2.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-22.2.0.tgz#2f4e4bff252bc6999f213288ed717940b27b0918" + integrity sha512-0Ax7zeqqbQL6Zcpo1WAvrqWQAnGsLB4tmQUUwsb5Cfo05XaQ78LWUUjaO4um7qaddKpZfk0vXlGcRVwtedpWfg== dependencies: - "@puppeteer/browsers" "1.4.6" - cosmiconfig "8.2.0" - puppeteer-core "20.9.0" + "@puppeteer/browsers" "2.1.0" + cosmiconfig "9.0.0" + puppeteer-core "22.2.0" qrcode@1.5.0: version "1.5.0" @@ -10335,7 +10328,7 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" -"semver@2 || 3 || 4 || 5", semver@7.5.2, semver@7.x, semver@^5.5.0, semver@^6.0.0, semver@^6.3.0, semver@^6.3.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.1, semver@^7.5.3, semver@^7.5.4: +"semver@2 || 3 || 4 || 5", semver@7.5.2, semver@7.6.0, semver@7.x, semver@^5.5.0, semver@^6.0.0, semver@^6.3.0, semver@^6.3.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.1, semver@^7.5.3, semver@^7.5.4: version "7.5.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== @@ -10595,7 +10588,7 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socks-proxy-agent@^8.0.1, socks-proxy-agent@^8.0.2: +socks-proxy-agent@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad" integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g== @@ -10999,14 +10992,16 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar-fs@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" - integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== +tar-fs@3.0.5, tar-fs@^3.0.4: + version "3.0.5" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.5.tgz#f954d77767e4e6edf973384e1eb95f8f81d64ed9" + integrity sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg== dependencies: - mkdirp-classic "^0.5.2" pump "^3.0.0" tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" tar-fs@^2.0.0: version "2.1.1" @@ -11018,17 +11013,6 @@ tar-fs@^2.0.0: pump "^3.0.0" tar-stream "^2.1.4" -tar-fs@^3.0.4: - version "3.0.5" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.5.tgz#f954d77767e4e6edf973384e1eb95f8f81d64ed9" - integrity sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg== - dependencies: - pump "^3.0.0" - tar-stream "^3.1.5" - optionalDependencies: - bare-fs "^2.1.1" - bare-path "^2.1.0" - tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -11694,6 +11678,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +urlpattern-polyfill@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz#f0a03a97bfb03cdf33553e5e79a2aadd22cac8ec" + integrity sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg== + use-callback-ref@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.1.tgz#9be64c3902cbd72b07fe55e56408ae3a26036fd0" @@ -12117,10 +12106,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -ws@8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +ws@8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== ws@^7.3.1, ws@^7.4.6: version "7.5.9" @@ -12205,10 +12194,10 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@17.7.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== +yargs@17.7.2, yargs@^17.1.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" @@ -12235,19 +12224,6 @@ yargs@^15.3.1, yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.1.1: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" From cadadb06f0df911042f2c8d0c9bc8590f8509549 Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:59:37 -0500 Subject: [PATCH 10/91] Update index.mdx (#6950) --- .../multi-user-data-access/index.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx index 39e2b5080ae..1517026a56b 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx @@ -21,10 +21,10 @@ If you want to grant a set of users access to a record, you use the `multipleOwn ```ts export const schema = a.schema({ Todo: a.model({ - content: a.string() + content: a.string(), }).authorization([ - a.allow.multipleOwners() - ]) + a.allow.multipleOwners(), + ]), }) ``` @@ -35,10 +35,10 @@ You can override the `inField` to a list of owners. Use this if you want a dynam ```ts export const schema = a.schema({ Todo: a.model({ - content: a.string() - authors: a.string().array() // record owner information now stored in "authors" field + content: a.string(), + authors: a.string().array(), // record owner information now stored in "authors" field }).authorization([ - a.allow.multipleOwners().inField("authors") - ]) + a.allow.multipleOwners().inField("authors"), + ]), }) ``` From bcfdc8eb7cbb29c12724727a4284f5a753fb0ef5 Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Thu, 22 Feb 2024 17:24:24 -0500 Subject: [PATCH 11/91] Update CODEOWNERS with correct PM alignment (#6951) --- .github/CODEOWNERS | 62 +++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 306ac4d87c7..4f6c6de7687 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,80 +9,80 @@ /src/pages/console/ @dbanksdesign @aws-amplify/documentation-team #Analytics -/src/**/**/analytics @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/analytics @hdworld11 @dbanksdesign @aws-amplify/documentation-team #Auth -/src/**/**/auth @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/**/**/authz @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/**/**/authentication @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/auth @josefaidt @aws-amplify/documentation-team +/src/**/**/authz @josefaidt @renebrandel @aws-amplify/documentation-team +/src/**/**/authentication @renebrandel @josefaidt @aws-amplify/documentation-team #Client Configuration -/src/**/**/client-configuration @renebrandel @dbanksdesign @hdworld11 @aws-amplify/documentation-team -/src/**/**/configuration @renebrandel @dbanksdesign @hdworld11 @aws-amplify/documentation-team +/src/**/**/client-configuration @renebrandel @josefaidt @hdworld11 @aws-amplify/documentation-team +/src/**/**/configuration @renebrandel @josefaidt @hdworld11 @aws-amplify/documentation-team #Common -/src/fragments/common @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/fragments/lib/common @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/fragments/common @renebrandel @hdworld11 @josefaidt @dbanksdesign @aws-amplify/documentation-team +/src/fragments/lib/common @renebrandel @hdworld11 @josefaidt @dbanksdesign @aws-amplify/documentation-team #Data -/src/**/**/datastore @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/**/**/data @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/datastore @renebrandel @aws-amplify/documentation-team +/src/**/**/data @renebrandel @aws-amplify/documentation-team #Debugging -/src/fragments/**/debugging @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/fragments/**/debugging @josefaidt @aws-amplify/documentation-team #Geo -/src/**/**/geo @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/geo @hdworld11 @aws-amplify/documentation-team #Getting Started -/src/**/start @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/start @renebrandel @hdworld11 @josefaidt @dbanksdesign @aws-amplify/documentation-team #GraphQL API -/src/**/**/api-graphql @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/**/**/graphqlapi @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/**/cli-legacy/graphql-transformer @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/fragments/sdk/api/**/graphql.mdx @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/api-graphql @renebrandel @aws-amplify/documentation-team +/src/**/**/graphqlapi @renebrandel @aws-amplify/documentation-team +/src/**/cli-legacy/graphql-transformer @renebrandel @aws-amplify/documentation-team +/src/fragments/sdk/api/**/graphql.mdx @renebrandel @aws-amplify/documentation-team #In-App Messaging -/src/**/**/in-app-messaging @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/in-app-messaging @hdworld11 @aws-amplify/documentation-team #Info -/src/fragments/**/info @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/fragments/**/info @renebrandel @hdworld11 @josefaidt @dbanksdesign @aws-amplify/documentation-team #Interactions -/src/**/**/interactions @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/interactions @josefaidt @aws-amplify/documentation-team #Logging -/src/fragments/lib/logging @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/fragments/lib/logging @hdworld11 @aws-amplify/documentation-team #Predictions -/src/**/**/predictions @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/predictions @hdworld11 @dbanksdesign @aws-amplify/documentation-team #Project Setup -/src/**/**/project-setup @renebrandel @dbanksdesign @hdworld11 @aws-amplify/documentation-team +/src/**/**/project-setup @renebrandel @josefaidt @dbanksdesign @hdworld11 @aws-amplify/documentation-team #PubSub -/src/**/**/pubsub @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/pubsub @renebrandel @aws-amplify/documentation-team #Push Notifications -/src/**/**/push-notifications @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/push-notifications @hdworld11 @dbanksdesign @aws-amplify/documentation-team #Rest API -/src/**/**/api-rest @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/**/**/restapi @renebrandel @dbanksdesign @aws-amplify/documentation-team -/src/fragments/sdk/api/**/rest.mdx @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/api-rest @josefaidt @aws-amplify/documentation-team +/src/**/**/restapi @josefaidt @aws-amplify/documentation-team +/src/fragments/sdk/api/**/rest.mdx @josefaidt @aws-amplify/documentation-team #SSR -/src/fragments/lib/ssr @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/fragments/lib/ssr @renebrandel @josefaidt @dbanksdesign @aws-amplify/documentation-team #Storage /src/**/**/storage @hdworld11 @aws-amplify/documentation-team #Troubleshooting -/src/fragments/lib/troubleshooting @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/fragments/lib/troubleshooting @josefaidt @aws-amplify/documentation-team #Utilities -/src/**/**/utilities @renebrandel @dbanksdesign @aws-amplify/documentation-team +/src/**/**/utilities @josefaidt @aws-amplify/documentation-team #Docs Engineering /src/components @aws-amplify/documentation-team From 5998b22f18cada98ba7c6d1f35c3f6b378637476 Mon Sep 17 00:00:00 2001 From: Elijah Quartey Date: Fri, 23 Feb 2024 11:27:15 -0700 Subject: [PATCH 12/91] fix(flutter, js): scope "connect existing cdk" guide to respective platform (#6947) Co-authored-by: Tim Nguyen <54393192+timngyn@users.noreply.github.com> --- .../build-a-backend/existing-resources/cdk/index.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx b/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx index cec421861d8..c194281e321 100644 --- a/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx +++ b/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx @@ -121,6 +121,8 @@ The CDK will deploy the stacks and display the following confirmation. Note the Now that you have built the backend API with the CDK, you can connect a frontend. Refer to section two to connect a React app or section three to connect a Flutter app, or complete both to connect frontends in each framework. + + ## Build a React app and connect to the GraphQL API In this section, we will connect a React web app to our existing GraphQL API. First, we will create a new React project and install the necessary Amplify packages. Next, we will use the Amplify CLI to generate GraphQL code matching our API structure. Then, we will add React components to perform queries and mutations to manage to-do items in our API. After that, we will configure the Amplify library with details of our backend API. Finally, we will run the application to demonstrate full CRUD functionality with our existing API. @@ -310,6 +312,10 @@ In this section, we generated GraphQL code, created React components, configured Congratulations on successfully creating a new React app and linking it to a preexisting GraphQL API built with AWS CDK. The next optional section will guide you through doing the same for a Flutter mobile app. Alternatively, you can skip ahead to the ["Clean up resources"](/[platform]/build-a-backend/existing-resources/cdk/#clean-up-resources) section. + + + + ## Build a Flutter app and connect to the GraphQL API In this section, we will connect a Flutter mobile app to the GraphQL API we created with the CDK. First, we will initialize a Flutter project, define models matching our schema, and use Amplify to integrate CRUD operations. Then we will add UI pages to manage to-do items with queries and mutations. Finally, we will configure Amplify with our backend details and run the app to demonstrate full functionality with our existing API. @@ -812,6 +818,8 @@ flutter run -d chrome Congratulations! You used the AWS Amplify Data CDK construct to create a GraphQL API backend using AWS AppSync. You then connected either a Flutter app, a React app, or both to that API using the Amplify libraries. If you have any feedback, leave a [GitHub issue](https://github.com/aws-amplify/docs/issues) or join our [Discord Community](https://discord.gg/amplify)! + + ## Clean up resources Once you're finished experimenting with these demo apps, we recommend deleting the backend resources to avoid incurring unexpected costs. You can do this by running the following command in the root folder of the CDK app created above. From c3d543296fa035761dac3ae7ba951d8e6728110f Mon Sep 17 00:00:00 2001 From: Elijah Quartey Date: Fri, 23 Feb 2024 12:24:41 -0700 Subject: [PATCH 13/91] =?UTF-8?q?Revert=20"fix(flutter,=20js):=20scope=20"?= =?UTF-8?q?connect=20existing=20cdk"=20guide=20to=20respective=20pl?= =?UTF-8?q?=E2=80=A6"=20(#6955)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5998b22f18cada98ba7c6d1f35c3f6b378637476. --- .../build-a-backend/existing-resources/cdk/index.mdx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx b/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx index c194281e321..cec421861d8 100644 --- a/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx +++ b/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx @@ -121,8 +121,6 @@ The CDK will deploy the stacks and display the following confirmation. Note the Now that you have built the backend API with the CDK, you can connect a frontend. Refer to section two to connect a React app or section three to connect a Flutter app, or complete both to connect frontends in each framework. - - ## Build a React app and connect to the GraphQL API In this section, we will connect a React web app to our existing GraphQL API. First, we will create a new React project and install the necessary Amplify packages. Next, we will use the Amplify CLI to generate GraphQL code matching our API structure. Then, we will add React components to perform queries and mutations to manage to-do items in our API. After that, we will configure the Amplify library with details of our backend API. Finally, we will run the application to demonstrate full CRUD functionality with our existing API. @@ -312,10 +310,6 @@ In this section, we generated GraphQL code, created React components, configured Congratulations on successfully creating a new React app and linking it to a preexisting GraphQL API built with AWS CDK. The next optional section will guide you through doing the same for a Flutter mobile app. Alternatively, you can skip ahead to the ["Clean up resources"](/[platform]/build-a-backend/existing-resources/cdk/#clean-up-resources) section. - - - - ## Build a Flutter app and connect to the GraphQL API In this section, we will connect a Flutter mobile app to the GraphQL API we created with the CDK. First, we will initialize a Flutter project, define models matching our schema, and use Amplify to integrate CRUD operations. Then we will add UI pages to manage to-do items with queries and mutations. Finally, we will configure Amplify with our backend details and run the app to demonstrate full functionality with our existing API. @@ -818,8 +812,6 @@ flutter run -d chrome Congratulations! You used the AWS Amplify Data CDK construct to create a GraphQL API backend using AWS AppSync. You then connected either a Flutter app, a React app, or both to that API using the Amplify libraries. If you have any feedback, leave a [GitHub issue](https://github.com/aws-amplify/docs/issues) or join our [Discord Community](https://discord.gg/amplify)! - - ## Clean up resources Once you're finished experimenting with these demo apps, we recommend deleting the backend resources to avoid incurring unexpected costs. You can do this by running the following command in the root folder of the CDK app created above. From 4c8a71162c7b7a0df15627c951e43fd570178a39 Mon Sep 17 00:00:00 2001 From: Heather Buchel Date: Fri, 23 Feb 2024 16:44:36 -0500 Subject: [PATCH 14/91] chore: remove duplicated logic between Layout and LayoutHeader (#6954) * chore: more layout refactoring * remove unused prop, alphabetize props --- src/components/Layout/Layout.tsx | 6 ++--- src/components/Layout/LayoutHeader.tsx | 35 ++++++-------------------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 41b5b57d891..836c5414bb0 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -228,10 +228,10 @@ export const Layout = ({ mainId={mainId} /> diff --git a/src/components/Layout/LayoutHeader.tsx b/src/components/Layout/LayoutHeader.tsx index 8310f5b2d67..2addc54ca34 100644 --- a/src/components/Layout/LayoutHeader.tsx +++ b/src/components/Layout/LayoutHeader.tsx @@ -2,7 +2,7 @@ import { useContext, useRef } from 'react'; import { useRouter } from 'next/router'; import { Button, Flex, View, VisuallyHidden } from '@aws-amplify/ui-react'; import classNames from 'classnames'; -import { DEFAULT_PLATFORM, PLATFORMS, Platform } from '@/data/platforms'; +import { Platform } from '@/data/platforms'; import { ALGOLIA_API_KEY, ALGOLIA_INDEX_NAME, @@ -11,7 +11,6 @@ import { import { IconMenu, IconDoubleChevron } from '@/components/Icons'; import { Menu } from '@/components/Menu'; import { LayoutContext } from '@/components/Layout'; -import type { HeadingInterface } from '@/components/TableOfContents/TableOfContents'; import { PlatformNavigator } from '@/components/PlatformNavigator'; import flatDirectory from 'src/directory/flatDirectory.json'; import { DocSearch } from '@docsearch/react'; @@ -22,43 +21,25 @@ import RepoActions from '../Menu/RepoActions'; import { usePathWithoutHash } from '@/utils/usePathWithoutHash'; export const LayoutHeader = ({ - hasTOC = true, - tocHeadings, + currentPlatform, + isGen2, pageType = 'inner', - platform, - showLastUpdatedDate = true + showLastUpdatedDate = true, + showTOC }: { - hasTOC?: boolean; - tocHeadings: HeadingInterface[]; + currentPlatform?: Platform | undefined; + isGen2?: boolean; pageType?: 'home' | 'inner'; - platform?: Platform; showLastUpdatedDate: boolean; + showTOC?: boolean; }) => { const { menuOpen, toggleMenuOpen } = useContext(LayoutContext); const menuButtonRef = useRef(null); const sidebarMenuButtonRef = useRef(null); - const showTOC = hasTOC && tocHeadings.length > 0; const router = useRouter(); const asPathWithNoHash = usePathWithoutHash(); - const isGen2 = asPathWithNoHash.split('/')[1] === 'gen2'; - let currentPlatform = isGen2 ? undefined : DEFAULT_PLATFORM; const isPrev = asPathWithNoHash.split('/')[2] === 'prev'; - if (!isGen2) { - // [platform] will always be the very first subpath right? - // when using `router.asPath` it returns a string that starts with a '/' - // To get the "platform" the client was trying to visit, we have to get the string at index 1 - // Doing this because when visiting a 404 page, there is no `router.query.platform`, so we have - // to check where the user was trying to visit from - const asPathPlatform = asPathWithNoHash.split('/')[1] as Platform; - - currentPlatform = platform - ? platform - : PLATFORMS.includes(asPathPlatform) - ? asPathPlatform - : DEFAULT_PLATFORM; - } - const handleMenuToggle = () => { if (!menuOpen) { toggleMenuOpen(true); From bb954cdf9a693cf8fc73ff309e30b2eec5302374 Mon Sep 17 00:00:00 2001 From: josefaidt Date: Fri, 23 Feb 2024 16:15:54 -0800 Subject: [PATCH 15/91] extend eslint to lint mdx files, snippets. enable in vscode --- .eslintrc.json | 36 +- .prettierignore | 2 +- .remarkrc | 8 + .vscode/extensions.json | 2 +- .vscode/settings.json | 22 +- package.json | 11 +- yarn.lock | 1473 +++++++++++++++++++++++++++++++++++++-- 7 files changed, 1502 insertions(+), 52 deletions(-) create mode 100644 .remarkrc diff --git a/.eslintrc.json b/.eslintrc.json index 65e2fac75f7..f4827915b4b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,14 +2,44 @@ "extends": [ "next/core-web-vitals", "plugin:@typescript-eslint/recommended", + "plugin:react/recommended", + "plugin:react-hooks/recommended", "prettier" // prettier should always come last ], "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint", "prettier"], "rules": { - "react-hooks/exhaustive-deps": "error", "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/consistent-type-exports": [ + "error", + { + "fixMixedExportsWithInlineTypeSpecifier": false + } + ], + "@typescript-eslint/consistent-type-imports": [ + "error", + { + "prefer": "type-imports" + } + ], "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "error" - } + "react-hooks/exhaustive-deps": "error" + }, + "overrides": [ + { + "files": ["*.mdx"], + "extends": ["plugin:mdx/recommended"], + "settings": { + "mdx/code-blocks": true, + // optional, if you want to disable language mapper, set it to `false` + // if you want to override the default language mapper inside, you can provide your own + "mdx/language-mapper": {} + }, + "rules": { + // for mdx shortcodes, and snippets + "react/jsx-no-undef": ["off"] + } + } + ] } diff --git a/.prettierignore b/.prettierignore index b576a9e8c58..01f5cd1b747 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,4 +9,4 @@ README-assets yarn.lock redirects.json *.md -*.mdx +# *.mdx diff --git a/.remarkrc b/.remarkrc new file mode 100644 index 00000000000..856dea4352e --- /dev/null +++ b/.remarkrc @@ -0,0 +1,8 @@ +{ + "plugins": [ + "preset-lint-consistent", + "preset-lint-markdown-style-guide", + "preset-lint-recommended", + "preset-prettier" + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 892cfcf05a8..d9800182eb9 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,7 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. - "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"], + "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "unifiedjs.vscode-remark"], // 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 0f43bf54870..ed6af970765 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,7 +9,23 @@ "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, - "[mdx]": { - "editor.wordWrap": "on" - } + "eslint.enable": true, + "eslint.probe": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "html", + "markdown", + "mdx" + ], + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "html", + "markdown", + "mdx" + ] } diff --git a/package.json b/package.json index 4922a142c58..e5140604569 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/react": "^18.0.0", "@types/url-parse": "^1.4.3", "@typescript-eslint/eslint-plugin": "^6.13.1", + "@typescript-eslint/parser": "^7.0.2", "axios": "^1.3.4", "classnames": "^2.3.2", "cross-env": "^7.0.3", @@ -47,8 +48,11 @@ "eslint": "8.54.0", "eslint-config-next": "14.0.3", "eslint-config-prettier": "^9.0.0", + "eslint-plugin-mdx": "^3.1.5", "eslint-plugin-next": "^0.0.0", "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.6.0", "fs-extra": "^9.0.1", "git-jiggy": "1.1.1", "husky": "^8.0.1", @@ -63,9 +67,15 @@ "rehype-img-size": "^1.0.1", "rehype-mdx-code-props": "^2.0.0", "remark": "^14.0.2", + "remark-cli": "^12.0.0", "remark-gfm": "^3.0.0", + "remark-lint": "^9.1.2", "remark-mdx": "^2.3.0", "remark-mdx-searchable": "^0.1.3", + "remark-preset-lint-consistent": "^5.1.2", + "remark-preset-lint-markdown-style-guide": "^5.1.3", + "remark-preset-lint-recommended": "^6.1.3", + "remark-preset-prettier": "^2.0.1", "rollup-plugin-node-polyfills": "^0.2.1", "sass": "^1.54.8", "serve": "^14.2.1", @@ -80,7 +90,6 @@ "@babel/core": "^7.23.2", "@babel/traverse": "^7.23.2", "loader-utils": "2.0.4", - "minimatch": "3.1.2", "decode-uri-component": "0.2.1", "fast-xml-parser": "4.2.5", "semver": "7.5.2", diff --git a/yarn.lock b/yarn.lock index 6aee437ac0d..77be5d96698 100644 --- a/yarn.lock +++ b/yarn.lock @@ -911,7 +911,7 @@ dependencies: tslib "^2.3.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.23.5": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== @@ -2014,6 +2014,35 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@npmcli/config@^8.0.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/config/-/config-8.1.0.tgz#2c7f6f80d78b9c18d8a70ae7c5fdb481be727bb0" + integrity sha512-61LNEybTFaa9Z/f8y6X9s2Blc75aijZK67LxqC5xicBcfkw8M/88nYrRXGXxAUKm6GRlxTZ216dp1UK2+TbaYw== + dependencies: + "@npmcli/map-workspaces" "^3.0.2" + ci-info "^4.0.0" + ini "^4.1.0" + nopt "^7.0.0" + proc-log "^3.0.0" + read-package-json-fast "^3.0.2" + semver "^7.3.5" + walk-up-path "^3.0.1" + +"@npmcli/map-workspaces@^3.0.2": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-3.0.4.tgz#15ad7d854292e484f7ba04bc30187a8320dba799" + integrity sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg== + dependencies: + "@npmcli/name-from-folder" "^2.0.0" + glob "^10.2.2" + minimatch "^9.0.0" + read-package-json-fast "^3.0.0" + +"@npmcli/name-from-folder@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz#c44d3a7c6d5c184bb6036f4d5995eee298945815" + integrity sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -2896,6 +2925,13 @@ dependencies: "@babel/types" "^7.20.7" +"@types/concat-stream@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-2.0.3.tgz#1f5c2ad26525716c181191f7ed53408f78eb758e" + integrity sha512-3qe4oQAPNwVNwK4C9c8u+VJqv9kez+2MR4qJpoPFfXtgxxif1QbFusvXzK0/Wra2VX07smostI2VMmJNSpZjuQ== + dependencies: + "@types/node" "*" + "@types/debug@^4.0.0": version "4.1.12" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" @@ -2943,6 +2979,11 @@ dependencies: "@types/unist" "*" +"@types/is-empty@^1.0.0": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/is-empty/-/is-empty-1.2.3.tgz#a2d55ea8a5ec57bf61e411ba2a9e5132fe4f0899" + integrity sha512-4J1l5d79hoIvsrKh5VUKVRA1aIdsOb10Hu5j3J2VfP/msDnfTdGPmNp2E1Wg+vs97Bktzo+MZePFFXSGoykYJw== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" @@ -3021,6 +3062,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.82.tgz#58d734b4acaa5be339864bbec9cd8024dd0b43d5" integrity sha512-pcDZtkx9z8XYV+ius2P3Ot2VVrcYOfXffBQUBuiszrlUzKSmoDYqo+mV+IoL8iIiIjjtOMvNSmH1hwJ+Q+f96Q== +"@types/node@^20.0.0": + version "20.11.20" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.20.tgz#f0a2aee575215149a62784210ad88b3a34843659" + integrity sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg== + dependencies: + undici-types "~5.26.4" + "@types/normalize-package-data@^2.4.0": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" @@ -3077,6 +3125,16 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/supports-color@^8.0.0": + version "8.1.3" + resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.3.tgz#b769cdce1d1bb1a3fa794e35b62c62acdf93c139" + integrity sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg== + +"@types/text-table@^0.2.0": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.5.tgz#f9c609b81c943e9fc8d73ef82ad2f2a78be5f53b" + integrity sha512-hcZhlNvMkQG/k1vcZ6yHOl6WAYftQ2MLfTHcYRZ2xYZFD8tGVnE3qFV0lj1smQeDSR7/yY0PyuUalauf33bJeA== + "@types/triple-beam@^1.3.2": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" @@ -3149,6 +3207,17 @@ "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" +"@typescript-eslint/parser@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.0.2.tgz#95c31233d343db1ca1df8df7811b5b87ca7b1a68" + integrity sha512-GdwfDglCxSmU+QTS9vhz2Sop46ebNCXpPPvsByK7hu0rFGRHL+AusKQJ7SoN+LbLh6APFpQwHKmDSwN35Z700Q== + dependencies: + "@typescript-eslint/scope-manager" "7.0.2" + "@typescript-eslint/types" "7.0.2" + "@typescript-eslint/typescript-estree" "7.0.2" + "@typescript-eslint/visitor-keys" "7.0.2" + debug "^4.3.4" + "@typescript-eslint/scope-manager@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" @@ -3157,6 +3226,14 @@ "@typescript-eslint/types" "6.21.0" "@typescript-eslint/visitor-keys" "6.21.0" +"@typescript-eslint/scope-manager@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.0.2.tgz#6ec4cc03752758ddd1fdaae6fbd0ed9a2ca4fe63" + integrity sha512-l6sa2jF3h+qgN2qUMjVR3uCNGjWw4ahGfzIYsCtFrQJCjhbrDPdiihYT8FnnqFwsWX+20hK592yX9I2rxKTP4g== + dependencies: + "@typescript-eslint/types" "7.0.2" + "@typescript-eslint/visitor-keys" "7.0.2" + "@typescript-eslint/type-utils@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e" @@ -3172,6 +3249,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== +"@typescript-eslint/types@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.0.2.tgz#b6edd108648028194eb213887d8d43ab5750351c" + integrity sha512-ZzcCQHj4JaXFjdOql6adYV4B/oFOFjPOC9XYwCaZFRvqN8Llfvv4gSxrkQkd2u4Ci62i2c6W6gkDwQJDaRc4nA== + "@typescript-eslint/typescript-estree@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" @@ -3186,6 +3268,20 @@ semver "^7.5.4" ts-api-utils "^1.0.1" +"@typescript-eslint/typescript-estree@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.2.tgz#3c6dc8a3b9799f4ef7eca0d224ded01974e4cb39" + integrity sha512-3AMc8khTcELFWcKcPc0xiLviEvvfzATpdPj/DXuOGIdQIIFybf4DMT1vKRbuAEOFMwhWt7NFLXRkbjsvKZQyvw== + dependencies: + "@typescript-eslint/types" "7.0.2" + "@typescript-eslint/visitor-keys" "7.0.2" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + "@typescript-eslint/utils@6.21.0": version "6.21.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134" @@ -3207,7 +3303,15 @@ "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" -"@ungap/structured-clone@^1.2.0": +"@typescript-eslint/visitor-keys@7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.2.tgz#2899b716053ad7094962beb895d11396fc12afc7" + integrity sha512-8Y+YiBmqPighbm5xA2k4wKTxRzx9EkBu7Rlw+WHqMvRJ3RPz/BMBO9b2ru0LUNmXg120PHUXD5+SWFy2R8DqlQ== + dependencies: + "@typescript-eslint/types" "7.0.2" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.0.0", "@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== @@ -3235,6 +3339,11 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + accepts@~1.3.5: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -3271,7 +3380,7 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.0, acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.9.0: +acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.3, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -3870,6 +3979,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + 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" @@ -4063,7 +4179,7 @@ chalk@5.0.1: resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== -chalk@5.3.0, chalk@^5.0.1: +chalk@5.3.0, chalk@^5.0.0, chalk@^5.0.1: version "5.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== @@ -4136,11 +4252,21 @@ character-entities-legacy@^3.0.0: resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + character-entities@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + character-reference-invalid@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" @@ -4151,7 +4277,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -"chokidar@>=3.0.0 <4.0.0": +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.0.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -4189,6 +4315,11 @@ ci-info@^3.8.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +ci-info@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" + integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + cjs-module-lexer@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" @@ -4479,6 +4610,16 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1" + integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.0.2" + typedarray "^0.0.6" + configstore@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -5046,6 +5187,11 @@ emittery@^0.7.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emoji-regex@^10.2.1: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -5099,7 +5245,7 @@ env-paths@^2.2.1: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -error-ex@^1.3.1: +error-ex@^1.3.1, error-ex@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -5314,6 +5460,26 @@ eslint-import-resolver-typescript@^3.5.2: is-core-module "^2.11.0" is-glob "^4.0.3" +eslint-mdx@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/eslint-mdx/-/eslint-mdx-3.1.5.tgz#e0276cad5649a4a174ffb27a7fbca83be7f580cb" + integrity sha512-ynztX0k7CQ3iDL7fDEIeg3g0O/d6QPv7IBI9fdYLhXp5fAp0fi8X22xF/D3+Pk0f90R27uwqa1clHpay6t0l8Q== + dependencies: + acorn "^8.11.3" + acorn-jsx "^5.3.2" + espree "^9.6.1" + estree-util-visit "^2.0.0" + remark-mdx "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + synckit "^0.9.0" + tslib "^2.6.2" + unified "^11.0.4" + unified-engine "^11.2.0" + unist-util-visit "^5.0.0" + uvu "^0.5.6" + vfile "^6.0.1" + 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" @@ -5366,6 +5532,27 @@ eslint-plugin-jsx-a11y@^6.7.1: object.entries "^1.1.7" object.fromentries "^2.0.7" +eslint-plugin-markdown@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-markdown/-/eslint-plugin-markdown-3.0.1.tgz#fc6765bdb5f82a75e2438d7fac619602f2abc38c" + integrity sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A== + dependencies: + mdast-util-from-markdown "^0.8.5" + +eslint-plugin-mdx@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-mdx/-/eslint-plugin-mdx-3.1.5.tgz#8f20d899c24272c0d471715c1f80d1332ec933c4" + integrity sha512-lUE7tP7IrIRHU3gTtASDe5u4YM2SvQveYVJfuo82yn3MLh/B/v05FNySURCK4aIxIYF1QYo3IRemQG/lyQzpAg== + dependencies: + eslint-mdx "^3.1.5" + eslint-plugin-markdown "^3.0.1" + remark-mdx "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + tslib "^2.6.2" + unified "^11.0.4" + vfile "^6.0.1" + eslint-plugin-next@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/eslint-plugin-next/-/eslint-plugin-next-0.0.0.tgz#f9ef680e8f68763c716ab44697c4b3cc3e0b2069" @@ -5379,7 +5566,7 @@ eslint-plugin-prettier@^5.0.1: prettier-linter-helpers "^1.0.0" synckit "^0.8.6" -"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": +"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705", eslint-plugin-react-hooks@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== @@ -5546,6 +5733,14 @@ estree-util-visit@^1.0.0: "@types/estree-jsx" "^1.0.0" "@types/unist" "^2.0.0" +estree-util-visit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-2.0.0.tgz#13a9a9f40ff50ed0c022f831ddf4b58d05446feb" + integrity sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/unist" "^3.0.0" + estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -6138,7 +6333,7 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^10.3.10: +glob@^10.0.0, glob@^10.2.2, glob@^10.3.10: version "10.3.10" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== @@ -6539,7 +6734,7 @@ ieee754@^1.1.13, ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0, ignore@^5.2.4: +ignore@^5.0.0, ignore@^5.2.0, ignore@^5.2.4: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -6582,6 +6777,16 @@ import-meta-resolve@^2.2.2: resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9" integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA== +import-meta-resolve@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-3.1.1.tgz#75d194ae465d17c15736f414734310c87d4c45d7" + integrity sha512-qeywsE/KC3w9Fd2ORrRDUw6nS/nLwZpXgfrOc2IILvZYnCaEMd+D56Vfg9k4G29gIeVi3XKql1RQatME8iYsiw== + +import-meta-resolve@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz#0b1195915689f60ab00f830af0f15cc841e8919e" + integrity sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA== + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -6610,6 +6815,11 @@ ini@^1.3.4, ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + inline-style-parser@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" @@ -6670,11 +6880,24 @@ is-accessor-descriptor@^1.0.1: dependencies: hasown "^2.0.0" +is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + is-alphabetical@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-alphanumerical@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" @@ -6781,6 +7004,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + is-decimal@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" @@ -6807,6 +7035,11 @@ 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-empty@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-empty/-/is-empty-1.2.0.tgz#de9bb5b278738a05a0b09a57e1fb4d4a341a9f6b" + integrity sha512-F2FnH/otLNJv0J6wc73A5Xo7oHLNnqplYqZhUu01tD54DIPvxIRSTSLkrUB/M0nHO4vo1O9PDfN4KoTxCzLh/w== + 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" @@ -6860,6 +7093,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + is-hexadecimal@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" @@ -7601,6 +7839,11 @@ json-parse-even-better-errors@^2.3.0: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-parse-even-better-errors@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz#02bb29fb5da90b5444581749c22cedd3597c6cb0" + integrity sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -7616,7 +7859,7 @@ json-stable-stringify-without-jsonify@^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== -json5@2.x, json5@^2.1.2, json5@^2.2.2, json5@^2.2.3: +json5@2.x, json5@^2.0.0, json5@^2.1.2, json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -7740,6 +7983,11 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lines-and-columns@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" + integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== + lint-staged@^14.0.0: version "14.0.1" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-14.0.1.tgz#57dfa3013a3d60762d9af5d9c83bdb51291a6232" @@ -7768,6 +8016,14 @@ listr2@6.6.1: rfdc "^1.3.0" wrap-ansi "^8.1.0" +load-plugin@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/load-plugin/-/load-plugin-6.0.2.tgz#a3ec9e04fb6009cf97f226e4695bc0f7037a9280" + integrity sha512-3KRkTvCOsyNrx4zvBl/+ZMqPdVyp26TIf6xkmfEGuGwCfNQ/HzhktwbJCxd1KJpzPbK42t/WVOL3cX+TDaMRuQ== + dependencies: + "@npmcli/config" "^8.0.0" + import-meta-resolve "^4.0.0" + loader-utils@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" @@ -7934,11 +8190,24 @@ markdown-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3" integrity sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q== +markdown-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz#34bebc83e9938cae16e0e017e4a9814a8330d3c4" + integrity sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== + markdown-table@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== +mdast-comment-marker@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-comment-marker/-/mdast-comment-marker-2.1.2.tgz#48ae16a49574bb22b489d04365ca3b1b5173f0da" + integrity sha512-HED3ezseRVkBzZ0uK4q6RJMdufr/2p3VfVZstE3H1N9K8bwtspztWo6Xd7rEatuGNoCXaBna8oEqMwUn0Ve1bw== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-mdx-expression "^1.1.0" + mdast-util-definitions@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz#9910abb60ac5d7115d6819b57ae0bcef07a3f7a7" @@ -7958,6 +8227,17 @@ mdast-util-find-and-replace@^2.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.0.0" +mdast-util-from-markdown@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c" + integrity sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^2.0.0" + micromark "~2.11.0" + parse-entities "^2.0.0" + unist-util-stringify-position "^2.0.0" + mdast-util-from-markdown@^1.0.0, mdast-util-from-markdown@^1.1.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz#9421a5a247f10d31d2faed2a30df5ec89ceafcf0" @@ -8052,7 +8332,14 @@ mdast-util-gfm@^2.0.0: mdast-util-gfm-task-list-item "^1.0.0" mdast-util-to-markdown "^1.0.0" -mdast-util-mdx-expression@^1.0.0: +mdast-util-heading-style@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-heading-style/-/mdast-util-heading-style-2.0.1.tgz#078cb0120a06af777c60413f7ba10f6d071b4672" + integrity sha512-0L5rthU4xKDVbw+UQ7D8Y8xOEsX4JXZvemWoEAsL+WAaeSH+TvVVwFnTb3G/OrjyP4VYQULoNWU+PdZfkmNu4A== + dependencies: + "@types/mdast" "^3.0.0" + +mdast-util-mdx-expression@^1.0.0, mdast-util-mdx-expression@^1.1.0: version "1.3.2" resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz#d027789e67524d541d6de543f36d51ae2586f220" integrity sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA== @@ -8123,6 +8410,17 @@ mdast-util-mdx@^2.0.0: mdast-util-mdxjs-esm "^1.0.0" mdast-util-to-markdown "^1.0.0" +mdast-util-mdx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz#792f9cf0361b46bee1fdf1ef36beac424a099c41" + integrity sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + mdast-util-to-markdown "^2.0.0" + mdast-util-mdxjs-esm@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz#645d02cd607a227b49721d146fd81796b2e2d15b" @@ -8204,6 +8502,11 @@ mdast-util-to-markdown@^2.0.0: unist-util-visit "^5.0.0" zwitch "^2.0.0" +mdast-util-to-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz#b8cfe6a713e1091cb5b728fc48885a4767f8b97b" + integrity sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w== + mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz#66f7bb6324756741c5f47a53557f0cbf16b6f789" @@ -8365,6 +8668,20 @@ micromark-extension-mdx-expression@^1.0.0: micromark-util-types "^1.0.0" uvu "^0.5.0" +micromark-extension-mdx-expression@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.0.tgz#1407b9ce69916cf5e03a196ad9586889df25302a" + integrity sha512-sI0nwhUDz97xyzqJAbHQhp5TfaxEvZZZ2JDqUo+7NvyIYG6BZ5CPPqj2ogUoPJlmXHBnyZUzISg9+oUmU6tUjQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-extension-mdx-jsx@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz#e72d24b7754a30d20fb797ece11e2c4e2cae9e82" @@ -8381,6 +8698,22 @@ micromark-extension-mdx-jsx@^1.0.0: uvu "^0.5.0" vfile-message "^3.0.0" +micromark-extension-mdx-jsx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.0.tgz#4aba0797c25efb2366a3fd2d367c6b1c1159f4f5" + integrity sha512-uvhhss8OGuzR4/N17L1JwvmJIpPhAd8oByMawEKx6NVdBCbesjH4t+vjEp3ZXft9DwvlKSD07fCeI44/N0Vf2w== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + micromark-extension-mdx-md@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz#595d4b2f692b134080dca92c12272ab5b74c6d1a" @@ -8388,6 +8721,13 @@ micromark-extension-mdx-md@^1.0.0: dependencies: micromark-util-types "^1.0.0" +micromark-extension-mdx-md@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz#1d252881ea35d74698423ab44917e1f5b197b92d" + integrity sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ== + dependencies: + micromark-util-types "^2.0.0" + micromark-extension-mdxjs-esm@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz#e4f8be9c14c324a80833d8d3a227419e2b25dec1" @@ -8403,6 +8743,21 @@ micromark-extension-mdxjs-esm@^1.0.0: uvu "^0.5.0" vfile-message "^3.0.0" +micromark-extension-mdxjs-esm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz#de21b2b045fd2059bd00d36746081de38390d54a" + integrity sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + micromark-extension-mdxjs@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz#f78d4671678d16395efeda85170c520ee795ded8" @@ -8417,6 +8772,20 @@ micromark-extension-mdxjs@^1.0.0: micromark-util-combine-extensions "^1.0.0" micromark-util-types "^1.0.0" +micromark-extension-mdxjs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz#b5a2e0ed449288f3f6f6c544358159557549de18" + integrity sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== + dependencies: + acorn "^8.0.0" + acorn-jsx "^5.0.0" + micromark-extension-mdx-expression "^3.0.0" + micromark-extension-mdx-jsx "^3.0.0" + micromark-extension-mdx-md "^2.0.0" + micromark-extension-mdxjs-esm "^3.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-destination@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz#eb815957d83e6d44479b3df640f010edad667b9f" @@ -8469,6 +8838,20 @@ micromark-factory-mdx-expression@^1.0.0: uvu "^0.5.0" vfile-message "^3.0.0" +micromark-factory-mdx-expression@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.1.tgz#f2a9724ce174f1751173beb2c1f88062d3373b1b" + integrity sha512-F0ccWIUHRLRrYp5TC9ZYXmZo+p2AM13ggbsW4T0b5CRKP8KHVRB8t4pwtBgTxtjRmwrK0Irwm7vs2JOZabHZfg== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + micromark-factory-space@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz#c8f40b0640a0150751d3345ed885a080b0d15faf" @@ -8647,6 +9030,20 @@ micromark-util-events-to-acorn@^1.0.0: uvu "^0.5.0" vfile-message "^3.0.0" +micromark-util-events-to-acorn@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.2.tgz#4275834f5453c088bd29cd72dfbf80e3327cec07" + integrity sha512-Fk+xmBrOv9QZnEDguL9OI9/NQQp6Hz4FuQ4YmCb/5V7+9eAh1s6AYSvL20kHkD67YIg7EpE54TiSlcsf3vyZgA== + dependencies: + "@types/acorn" "^4.0.0" + "@types/estree" "^1.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + estree-util-visit "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + micromark-util-html-tag-name@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz#48fd7a25826f29d2f71479d3b4e83e94829b3588" @@ -8789,6 +9186,14 @@ micromark@^4.0.0: micromark-util-symbol "^2.0.0" micromark-util-types "^2.0.0" +micromark@~2.11.0: + version "2.11.4" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-2.11.4.tgz#d13436138eea826383e822449c9a5c50ee44665a" + integrity sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA== + dependencies: + debug "^4.0.0" + parse-entities "^2.0.0" + micromatch@4.0.5, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -8860,14 +9265,28 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@3.1.2, minimatch@9.0.3, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2, minimatch@^5.0.1, minimatch@^9.0.1: +minimatch@3.1.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== dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: +minimatch@9.0.3, minimatch@^9.0.0, minimatch@^9.0.1: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.0.0, minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -9084,6 +9503,13 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +nopt@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== + dependencies: + abbrev "^2.0.0" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -9106,6 +9532,11 @@ 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== +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -9404,6 +9835,18 @@ parent-module@^2.0.0: dependencies: callsites "^3.1.0" +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-entities@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e" @@ -9436,6 +9879,17 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-7.1.1.tgz#68f7e6f0edf88c54ab14c00eb700b753b14e2120" + integrity sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw== + dependencies: + "@babel/code-frame" "^7.21.4" + error-ex "^1.3.2" + json-parse-even-better-errors "^3.0.0" + lines-and-columns "^2.0.3" + type-fest "^3.8.0" + parse5@6.0.1, parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -9556,6 +10010,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + pngjs@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb" @@ -9642,6 +10101,11 @@ prismjs@^1.27.0: resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + progress@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -9857,6 +10321,14 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" +read-package-json-fast@^3.0.0, read-package-json-fast@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz#394908a9725dc7a5f14e70c8e7556dff1d2b1049" + integrity sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw== + dependencies: + json-parse-even-better-errors "^3.0.0" + npm-normalize-package-bin "^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" @@ -9876,7 +10348,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.0.2, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -9995,6 +10467,16 @@ rehype@^11.0.0: rehype-stringify "^8.0.0" unified "^9.0.0" +remark-cli@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/remark-cli/-/remark-cli-12.0.0.tgz#13919d1acf18028c3776f01a2e7cc1eb95b8b93a" + integrity sha512-IGxCo2VsXC/GS2YdlF7+S8DsUiyULyiauik01NFoiMIrOlbDhXjrKLD8hYazwQdD67nw2k7cwOBIxcK/cbNd9Q== + dependencies: + import-meta-resolve "^3.0.0" + markdown-extensions "^2.0.0" + remark "^15.0.0" + unified-args "^11.0.0" + remark-gfm@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f" @@ -10005,41 +10487,757 @@ remark-gfm@^3.0.0: micromark-extension-gfm "^2.0.0" unified "^10.0.0" -remark-mdx-searchable@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/remark-mdx-searchable/-/remark-mdx-searchable-0.1.3.tgz#2c8282823f76ddeca8d0aea8f8bee1690a8b33c5" - integrity sha512-xv0tTir4ing9fTIaI5k8LBSeYPn/hfGCu6PEI3V6komU/Qr2e2pHiDmxQjl0d+VknGwh83o1Rll4sZKukHC+iA== +remark-lint-blockquote-indentation@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-blockquote-indentation/-/remark-lint-blockquote-indentation-3.1.2.tgz#0d35b4da3731eb4885ea0e794e60d8ede8e1f78a" + integrity sha512-5DOrFsZd5dXqA4p/VZvWSrqIWNFbBXjX7IV/FkVkxlNhNF/0FMf/4v8x1I2W3mzaZ7yDsWS/egpZnmligq1ckQ== dependencies: - unist-util-visit "^2.0.3" + "@types/mdast" "^3.0.0" + pluralize "^8.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" -remark-mdx@^2.0.0, remark-mdx@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-2.3.0.tgz#efe678025a8c2726681bde8bf111af4a93943db4" - integrity sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g== +remark-lint-checkbox-character-style@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-checkbox-character-style/-/remark-lint-checkbox-character-style-4.1.2.tgz#84c1c980a6a1f05b79f299af5bdd8ac3352d1055" + integrity sha512-5ITz+1cCuJ3Jv/Q7rKgDEucCOnIgjWDnSHPJA1tb4TI/D316h+ALbDhZIpP8gyfAm6sBAh3Pwz9XZJN2uJB5UQ== dependencies: - mdast-util-mdx "^2.0.0" - micromark-extension-mdxjs "^1.0.0" + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" -remark-parse@^10.0.0: - version "10.0.2" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.2.tgz#ca241fde8751c2158933f031a4e3efbaeb8bc262" - integrity sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw== +remark-lint-code-block-style@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-code-block-style/-/remark-lint-code-block-style-3.1.2.tgz#5f2ba66240a1c890ebe7a5f84496029a67cff929" + integrity sha512-3wsWmzzdyEsB9sOzBOf46TSkwwVKXN2JpTEQb6feN0Tl6Vg75F7T9MHqMz7aqk/56bOXSxUzdpXDscGBhziLRA== dependencies: "@types/mdast" "^3.0.0" - mdast-util-from-markdown "^1.0.0" unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" -remark-rehype@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279" - integrity sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw== +remark-lint-definition-case@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-definition-case/-/remark-lint-definition-case-3.1.2.tgz#45c2810fb751ee821e31d6ab0614c25f5ac61314" + integrity sha512-/VxucJKEFykOe2ILgi0LLia0RaSyOPQXpR+tuX4MK3iKxIm7aT2oINgR9ugLpI15xJ463LyTi5mXf+BGveXeWA== dependencies: - "@types/hast" "^2.0.0" "@types/mdast" "^3.0.0" - mdast-util-to-hast "^12.1.0" unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" -remark-stringify@^10.0.0: +remark-lint-definition-spacing@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-definition-spacing/-/remark-lint-definition-spacing-3.1.2.tgz#8055497c0b293fa0a803f1a156d555e340fd4dc3" + integrity sha512-l058jAKfZfCOmlbIzoTll+CrZm9Bh42ZVCHcODPSZC8Yx4terCKgIoks+RWJDEdUbEw0YQoYvPc59ZVmp3BIew== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-emphasis-marker@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-emphasis-marker/-/remark-lint-emphasis-marker-3.1.2.tgz#f86034ce0641fcf38590a4cd83e310d491be6390" + integrity sha512-hPZ8vxZrIfxmLA5B66bA8y3PdHjcCQuaLsySIqi5PM2DkpN6a7zAP3v1znyRSaYJ1ANVWcu00/0bNzuUjflGCA== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-fenced-code-flag@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-fenced-code-flag/-/remark-lint-fenced-code-flag-3.1.2.tgz#a7b8040cdc78414f5fe742e2a28b6ef5dac14a79" + integrity sha512-yh4m3dlPmRsqM/BFhpqHYfrmBvFQ+D5dZZKDDYP2rf3YEoXlEVt8T8lWQueTTSxcq6yXAqL/XQL/iqqUHlLcHw== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-fenced-code-marker@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-fenced-code-marker/-/remark-lint-fenced-code-marker-3.1.2.tgz#6ffdba7f311066e4e42fdefbcbdb54aca28b0bba" + integrity sha512-6XNqjOuhT+0c7Q/22aCsMz61ne9g8HRpYF79EXQPdbzYa+PcfPXMiQKStONY3PfC8OE2/3WXI2zcs8w9x+8+VQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-file-extension@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-file-extension/-/remark-lint-file-extension-2.1.2.tgz#691aad28b9002107c3dc78d29fbbd62fc3d650a3" + integrity sha512-Nq54F5R7F1gyj/IMW6SvkAbVNrH+p38WK3//KCoZLDUYFrH0oXgXXFGHi9CT/O0VEopW+bWJfTn8YAJRs0qI5Q== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-final-definition@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-final-definition/-/remark-lint-final-definition-3.1.2.tgz#9a147b456df3049b8f32570265736a6cfea19a5a" + integrity sha512-3O3JT6xqlrgq+UjhMPxshgMtwXn99w0BEO9JwbDls49N0XCu0n22Pq1n6X3tEVzskPLo3YYyVYfW2Z2C2rneKQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-final-newline@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-final-newline/-/remark-lint-final-newline-2.1.2.tgz#c67267f6feede083f034d1f43ff22c5056bc0fe0" + integrity sha512-K0FdPGPyEB94PwNgopwVJFE8oRWi7IhY2ycXFVAMReI51el7EHB8F1gX14tB6p6zyGy6mUh69bCVU9mMTNeOUg== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-hard-break-spaces@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-hard-break-spaces/-/remark-lint-hard-break-spaces-3.1.2.tgz#c14cec655c697bcd76c1c65bdfb81a82b42b3160" + integrity sha512-HaW0xsl3TI7VFAqGWWcZtPqyz0NWu19KKjSO7OGFTUJU4S9YiRnhIxmSFM0ZLSsVAynE+dhzVKa8U7dOpWDcOg== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-heading-increment@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-heading-increment/-/remark-lint-heading-increment-3.1.2.tgz#bef7e7cf8a6292b2869a0cb03944094504d984ad" + integrity sha512-+fMfZmFh6ie6MmbRCVW77Rha15zDmnHWKiA0Do08OTrfngPTv8ZKXYLmxhUpL+xV9ts9q+9Kz5rv0L4QD4sEwQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-heading-style@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-heading-style/-/remark-lint-heading-style-3.1.2.tgz#62a191d14889be41d8cc8c8e0a150b6249d74286" + integrity sha512-0RkcRPV/H2bPFgeInzBkK1cWUwtFTm83I+Db/Z5tDY02GzKOosHLvxtJyj/1391/opAH1LYbHtHWffir99IUgw== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-heading-style "^2.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-link-title-style@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-link-title-style/-/remark-lint-link-title-style-3.1.2.tgz#c32d943ef5d0d2d3807e573786ecdae5c04787ee" + integrity sha512-if4MahYJVvQUWlrXDF8GSv4b9VtLSgMSDHeikQp1/hGYlihLl9uGw3nlL5Lf9DqTN0qaT6RPbXOjuuzHlk38sg== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile-location "^4.0.0" + +remark-lint-list-item-bullet-indent@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-list-item-bullet-indent/-/remark-lint-list-item-bullet-indent-4.1.2.tgz#20f0d147e2670956d7e09c8ed5b81212cc9c1f2f" + integrity sha512-WgU5nooqIEm6f35opcbHKBzWrdFJA3XcyTfB3nv/v0KX43/h6qFGmmMJ5kEiaFExuQp3dZSdatWuY0YZ9YRbUg== + dependencies: + "@types/mdast" "^3.0.0" + pluralize "^8.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-list-item-content-indent@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-list-item-content-indent/-/remark-lint-list-item-content-indent-3.1.2.tgz#ca9f2ebe58174ddb8da2bb7a1e767ad236d6992a" + integrity sha512-TB0pmrWiRaQW80Y/PILFQTnHDghRxXNzMwyawlP+DBF9gNom3pEBmb4ZlGQlN0aa3r8VWeIKdv1ylHrfXE0vqA== + dependencies: + "@types/mdast" "^3.0.0" + pluralize "^8.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-list-item-indent@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-list-item-indent/-/remark-lint-list-item-indent-3.1.2.tgz#8b7e8eafe78ea2308c61ee9ff239ecf46b6f2bd6" + integrity sha512-tkrra1pxZVE4OVJGfN435u/v0ljruXU+dHzWiKDYeifquD4aWhJxvSApu7+FbE098D/4usVXgMxwFkNhrpZcSQ== + dependencies: + "@types/mdast" "^3.0.0" + pluralize "^8.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-list-item-spacing@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-list-item-spacing/-/remark-lint-list-item-spacing-4.1.2.tgz#8366a288ee1560bc798e3e7e45d4ec36fe27c834" + integrity sha512-RHscGCa81PzcI09H0JAKXGyUiIMRTg5u4G8/p1zqnfEeOgG1R+87mLEJrOC9tUWGjuVoyd7T8Q2DMxg1Iep9ow== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-maximum-heading-length@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-maximum-heading-length/-/remark-lint-maximum-heading-length-3.1.2.tgz#20225d60dd7985f10dfcbac49decfac04c7ee160" + integrity sha512-gkmZxjlzEmNjBRBwef0L/Qmoabxxof0mryOxWzRZSu1xz4Qsp+UFWMhiHGXbE9WJL6EBW8yNTOpgnNgUOzqDiQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-maximum-line-length@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/remark-lint-maximum-line-length/-/remark-lint-maximum-line-length-3.1.3.tgz#9299a5282ba74c4937443c5267dcb473b7bff97c" + integrity sha512-TA7IE+0c8agRm1k7JZr7ZZFiL44JMBAj1KlMxSTACBuebdPJe7IPaLIQga10bnz75jfWMzSiRURMFHo4lt3kdw== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-blockquote-without-marker@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-blockquote-without-marker/-/remark-lint-no-blockquote-without-marker-5.1.2.tgz#647de3297d8bca29e6591a2ba24ba50a8d711a9c" + integrity sha512-QPbqsrt7EfpSWqTkZJ9tepabPIhBDlNqZkuxxMQYD0OQ2N+tHDUq3zE1JxI5ts1V9o/mWApgySocqGd3jlcKmQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile-location "^4.0.0" + +remark-lint-no-consecutive-blank-lines@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/remark-lint-no-consecutive-blank-lines/-/remark-lint-no-consecutive-blank-lines-4.1.3.tgz#929979b682c173472e23c94150e7c096cac83677" + integrity sha512-yU3jH6UMHvaxX3DPBen+7CoPiCcqJ4BeteyOKeKX+tKWCWKILpiz+TVToRbeLnWO4IvFNnSRFMSXmcWSDdbY4w== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + pluralize "^8.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-duplicate-definitions@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-duplicate-definitions/-/remark-lint-no-duplicate-definitions-3.1.2.tgz#6acb7b7cee7c8f063399a50c2c76ae19894d6057" + integrity sha512-vi0nXA7p+pjQOorZOkr9E+QDhG74JAdbzqglWPrWWNI3z2rUYWYHTNSyWJbwEXaIIcev1ZAw8SCAOis5MNm+pA== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-stringify-position "^3.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-duplicate-headings@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-duplicate-headings/-/remark-lint-no-duplicate-headings-3.1.2.tgz#3ab2fa5d71b91ad942dfb40712e404e8a6ea893d" + integrity sha512-atBlykGOx9BhpXGp0BUMWxn/T89+hC0Gel8xOIMaFkDhRcLlLVt+/F/aJGhM2Sp0R9NTQ6ejn+JYMLl5Aw2Z+g== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-stringify-position "^3.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-emphasis-as-heading@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-emphasis-as-heading/-/remark-lint-no-emphasis-as-heading-3.1.2.tgz#6810b1a26364fd18e4f0a42a62284750ce21cfdb" + integrity sha512-2DDx0VkqSExR6oqBiQtOsmdDwT7f3hpnPwPvBCk7BDeDU53JWY1kBAkRObkEptgH3GfpwxIQymIdHXesBpAQAg== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-file-name-articles@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-file-name-articles/-/remark-lint-no-file-name-articles-2.1.2.tgz#cb5823ef460a651f8486f11491d23d017724904c" + integrity sha512-kM4vwBkne7f9euDKsuyxTtrsiafjH+KOwu8ZmuSVWh5U+u0EMcPyN5fxfaQIW+5FkrJA1jwnRu7ciXJBJt74Og== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-no-file-name-consecutive-dashes@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-file-name-consecutive-dashes/-/remark-lint-no-file-name-consecutive-dashes-2.1.2.tgz#fceae4d1a3b86e2ddf0e0eaac2f90ee94e88b5e6" + integrity sha512-gw06jaaFwBR3s+3E2kJlv+E7rAzS7Nj+MFU7TViwbsYnR7PA96htLVDCjClyNUE7JHUNcv93HdLm8ykg8kRyNA== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-no-file-name-irregular-characters@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-file-name-irregular-characters/-/remark-lint-no-file-name-irregular-characters-2.1.2.tgz#8dbca858c37e46f08cf5051b5a55945381bf4aab" + integrity sha512-2tcqzLm39Jc4THNP2yvJruOz2HtV4yh+eePiySKmhfZk/6ifMyOF/wlKHKcswczSGE4InNTfxJnc/AoxOJEdkw== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-no-file-name-mixed-case@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-file-name-mixed-case/-/remark-lint-no-file-name-mixed-case-2.1.2.tgz#7b33fdc2c234ca2c20ef0e6244ca48a7f07d48f7" + integrity sha512-0mTrjxBB4/0rV7sef+xjV5Aeb6LuW19X4QbNHW2RW7aMy+mtgJU03wdb8Y0LTnWVFHjUbc+iHrsFeCA/Pu1kew== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-no-file-name-outer-dashes@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-file-name-outer-dashes/-/remark-lint-no-file-name-outer-dashes-2.1.2.tgz#452cdcbebaed68359a9e87a973d37a74f1c0db09" + integrity sha512-VrbHg25Oo9k/bNbS7ye1X7F6ER4uZSubO+t5DHJ4WZ6iVbNtBar/JwzVelY1YxUAutv42OvHfuveh4vKlcNgVA== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + +remark-lint-no-heading-content-indent@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-heading-content-indent/-/remark-lint-no-heading-content-indent-4.1.2.tgz#fdc57ed0b40209ed09d25366b61d445399f1562b" + integrity sha512-TTxFsm1f4ZHFxZQCuz7j0QK4RvP6oArTiwazKLr16yaZe1608ypogMek4A30j2xX8WuO9+2uBzLXCY5OBo5x5Q== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-heading-style "^2.0.0" + pluralize "^8.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-heading-punctuation@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-heading-punctuation/-/remark-lint-no-heading-punctuation-3.1.2.tgz#7bf808106de768467216c34db0dc586cb248056e" + integrity sha512-KnvHEVB/DcxJOhUvVteiovAy1+32YY5Vm0UBJqYCFrrHnN/y9ETvOJzlxFy47TaB8x2UyncSEg2JuT66UL4ONQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-inline-padding@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-inline-padding/-/remark-lint-no-inline-padding-4.1.2.tgz#f1e8bd21694f9f773078ff977934edea8ffd842d" + integrity sha512-dGyhWsiqCZS3Slob0EVBUfsFBbdpMIBCvb56LlCgaHbnLsnNYx8PpF/wA5CgsN8BXIbXfRpyPB5cIJwIq5taYg== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-literal-urls@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-literal-urls/-/remark-lint-no-literal-urls-3.1.2.tgz#9a261f810c5b5edbb3ed2903c3d03dc9c5e4b575" + integrity sha512-4tV9JGLKxAMFSuWDMOqLozkFJ3HyRvhzgrPrxASoziaml23m7UXAozk5dkIrFny1cN2oG988Z8tORxX2FL1Ilw== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-to-string "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-multiple-toplevel-headings@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-multiple-toplevel-headings/-/remark-lint-no-multiple-toplevel-headings-3.1.2.tgz#e4eb81f01b81e346431314df611c75213f019724" + integrity sha512-9rJSsrwdzwKmtuloBjJobLzjGL7Lgtk3+vMNUyuH9z/nBfkUCN3qxn3Nt9AxL+wwSAsHV6e74W+W2S1ohBLt6A== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-stringify-position "^3.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-shell-dollars@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-shell-dollars/-/remark-lint-no-shell-dollars-3.1.2.tgz#3663591611516a8eb3faf1251c95f7b9c14b1a02" + integrity sha512-np2MDEhXHviXhbQFjnC1QYv5/fxCV1cIHfGMoJpqiW7Zcu/UGCOo5TE3XswZH4ukHZJ65c3X2A6qfLDW+ur3CQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-shortcut-reference-image@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-shortcut-reference-image/-/remark-lint-no-shortcut-reference-image-3.1.2.tgz#bae29d978fc1fedfd750feb23bb00c3b28d56416" + integrity sha512-NX4XJFPyDeJJ77pmETxRj4oM/zayf7Lmn/O87HgExBkQIPz2NYbDeKD8QEyliLaV/oKA2rQufpzuFw55xa1Tww== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-shortcut-reference-link@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-shortcut-reference-link/-/remark-lint-no-shortcut-reference-link-3.1.2.tgz#451d867b58fcf20f6fcacf3da64fedf86b07bcf2" + integrity sha512-/9iPN7FLKaaIzw4tLWKu7Rx0wAP7E2EuzIeentQlkY0rO/mMHipmT3IlgiebsAInKagzTY6TNFoG1rq2VnaCcA== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-no-table-indentation@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-table-indentation/-/remark-lint-no-table-indentation-4.1.2.tgz#08ef417f7f280c11521ff85a5d3d243aad08e443" + integrity sha512-5lkO+Yrtni/CDMZi7mlwbB2zzRQLH94DesboXg51aO2UfZlSn5dZNhmN5wkyCU2AiApUhlFNbxfKMHOWFPLdog== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile-location "^4.0.0" + +remark-lint-no-undefined-references@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/remark-lint-no-undefined-references/-/remark-lint-no-undefined-references-4.2.1.tgz#191fbd4f05162b2595765351bcc75a41919e2fab" + integrity sha512-HdNg5b2KiuNplcuVvRtsrUiROw557kAG1CiZYB7jQrrVWFgd86lKTa3bDiywe+87dGrGmHd3qQ28eZYTuHz2Nw== + dependencies: + "@types/mdast" "^3.0.0" + micromark-util-normalize-identifier "^1.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + vfile-location "^4.0.0" + +remark-lint-no-unused-definitions@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-no-unused-definitions/-/remark-lint-no-unused-definitions-3.1.2.tgz#491dcef1dd284060f0222d11b9c38bd90c2d76fd" + integrity sha512-bOcaJAnjKxT3kASFquUA3fO9xem9wZhVqt8TbqjA84+G4n40qjaLXDs/4vq73aMsSde73K0f3j1u0pMe7et8yQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-visit "^4.0.0" + +remark-lint-ordered-list-marker-style@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-ordered-list-marker-style/-/remark-lint-ordered-list-marker-style-3.1.2.tgz#9c7b38fb80784a7bd966f888f87aa83bff282d14" + integrity sha512-62iVE/YQsA0Azaqt8yAJWPplWLS47kDLjXeC2PlRIAzCqbNt9qH3HId8vZ15QTSrp8rHmJwrCMdcqV6AZUi7gQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-ordered-list-marker-value@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-ordered-list-marker-value/-/remark-lint-ordered-list-marker-value-3.1.2.tgz#a2bb783c184a76b73eee2560916fa2bd43494239" + integrity sha512-kG08nhsFk8rhoXK5EeDN/wN28CxefraDud/MaZnji8LEyxF3HAkzFuETr9laOn8Ey+n8h/C0mpqAwUf4thyJ5g== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-rule-style@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-rule-style/-/remark-lint-rule-style-3.1.2.tgz#001c8aca23464bf68ff00c2bd0c73b1de557a61c" + integrity sha512-0CsX2XcX9pIhAP5N7Y8mhYXp3/Ld+NvxXY1p0LHAq0NZu17UsZLuegvx/s25uFbQs08DcmSqyKnepU9qGGqmTQ== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-strong-marker@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-strong-marker/-/remark-lint-strong-marker-3.1.2.tgz#55ff84a696f8453900daf511488f8f2e85edf551" + integrity sha512-U/g4wngmiI0Q6WBRQG6pZxnDS33Wt/0QYA3+KNFBDykoi1vXsDEorIqy3dEag9z6XHwcMvFDsff6VRUhaOJWQg== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-table-cell-padding@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/remark-lint-table-cell-padding/-/remark-lint-table-cell-padding-4.1.3.tgz#6ea87aebd8485824fe5c38a3400b93b1a4d56814" + integrity sha512-N9xtnS6MG/H3srAMjqqaF26A7socr87pIgt64dr5rxoSbDRWRPChGQ8y7wKyV8VeyRNF37e3E5KB3bQVqjSYaQ== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-table-pipe-alignment@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/remark-lint-table-pipe-alignment/-/remark-lint-table-pipe-alignment-3.1.3.tgz#660ff30617f834495e291ec93fe882ac2d67c239" + integrity sha512-bnE8WrB4kSrN+Yr+xN2GHWVgGukeSFU43qPMrpCzTyOSbzep366wORlFKqZmyFPEkIZ/uAUFS0Qm9DND66Yz/A== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-table-pipes@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-table-pipes/-/remark-lint-table-pipes-4.1.2.tgz#abdd6f9c4be7f7575a3054d06d57d8534b596ccf" + integrity sha512-Ex2cJDXA0hdD9CC5Nu0p3K5LP+AhzPvk4sIOSbevCTSRyCS/SkNk4CQ6pwWBxuPVuHQUkqXkT8lgu8wwr/9A3A== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint-unordered-list-marker-style@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/remark-lint-unordered-list-marker-style/-/remark-lint-unordered-list-marker-style-3.1.2.tgz#7675ad4ffd91613a51c5a908061d9eb79df63160" + integrity sha512-JFiyB4ZprJGGndCaFB8FssXd48m4Kh+CUqzNgu3lBLEiW8dEAGRlD9M2AzyyA+Q29WJP/FntDCbP22DeON91UA== + dependencies: + "@types/mdast" "^3.0.0" + unified "^10.0.0" + unified-lint-rule "^2.0.0" + unist-util-generated "^2.0.0" + unist-util-position "^4.0.0" + unist-util-visit "^4.0.0" + +remark-lint@^9.0.0, remark-lint@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/remark-lint/-/remark-lint-9.1.2.tgz#0781e273bba33fbfd26210b639b8a3702d65ad91" + integrity sha512-m9e/aPlh7tsvfJfj8tPxrQzD6oEdb9Foko+Ya/6OwUP9EoGMfehv1Qtv26W1DoH58Wn8rT8CD+KuprTWscMmIA== + dependencies: + "@types/mdast" "^3.0.0" + remark-message-control "^7.0.0" + unified "^10.1.0" + +remark-mdx-searchable@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/remark-mdx-searchable/-/remark-mdx-searchable-0.1.3.tgz#2c8282823f76ddeca8d0aea8f8bee1690a8b33c5" + integrity sha512-xv0tTir4ing9fTIaI5k8LBSeYPn/hfGCu6PEI3V6komU/Qr2e2pHiDmxQjl0d+VknGwh83o1Rll4sZKukHC+iA== + dependencies: + unist-util-visit "^2.0.3" + +remark-mdx@^2.0.0, remark-mdx@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-2.3.0.tgz#efe678025a8c2726681bde8bf111af4a93943db4" + integrity sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g== + dependencies: + mdast-util-mdx "^2.0.0" + micromark-extension-mdxjs "^1.0.0" + +remark-mdx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-3.0.1.tgz#8f73dd635c1874e44426e243f72c0977cf60e212" + integrity sha512-3Pz3yPQ5Rht2pM5R+0J2MrGoBSrzf+tJG94N+t/ilfdh8YLyyKYtidAYwTveB20BoHAcwIopOUqhcmh2F7hGYA== + dependencies: + mdast-util-mdx "^3.0.0" + micromark-extension-mdxjs "^3.0.0" + +remark-message-control@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/remark-message-control/-/remark-message-control-7.1.1.tgz#71e9b757b835fad2ac14fafa8b432f51b9b9bf52" + integrity sha512-xKRWl1NTBOKed0oEtCd8BUfH5m4s8WXxFFSoo7uUwx6GW/qdCy4zov5LfPyw7emantDmhfWn5PdIZgcbVcWMDQ== + dependencies: + "@types/mdast" "^3.0.0" + mdast-comment-marker "^2.0.0" + unified "^10.0.0" + unified-message-control "^4.0.0" + vfile "^5.0.0" + +remark-parse@^10.0.0: + version "10.0.2" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.2.tgz#ca241fde8751c2158933f031a4e3efbaeb8bc262" + integrity sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw== + dependencies: + "@types/mdast" "^3.0.0" + mdast-util-from-markdown "^1.0.0" + unified "^10.0.0" + +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-preset-lint-consistent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/remark-preset-lint-consistent/-/remark-preset-lint-consistent-5.1.2.tgz#d081ad91ab92367d280a4efe355d6fd0819d109e" + integrity sha512-RQrWBFmyIkKfXtp9P1Fui7UbGSfXth9nuvRJUVnO0vfevBJe02iyMZWPokXSwkDOI/cM539wj0i3vrQupz+v5A== + dependencies: + "@types/mdast" "^3.0.0" + remark-lint "^9.0.0" + remark-lint-blockquote-indentation "^3.0.0" + remark-lint-checkbox-character-style "^4.0.0" + remark-lint-code-block-style "^3.0.0" + remark-lint-emphasis-marker "^3.0.0" + remark-lint-fenced-code-marker "^3.0.0" + remark-lint-heading-style "^3.0.0" + remark-lint-link-title-style "^3.0.0" + remark-lint-list-item-content-indent "^3.0.0" + remark-lint-ordered-list-marker-style "^3.0.0" + remark-lint-rule-style "^3.0.0" + remark-lint-strong-marker "^3.0.0" + remark-lint-table-cell-padding "^4.0.0" + unified "^10.0.0" + +remark-preset-lint-markdown-style-guide@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/remark-preset-lint-markdown-style-guide/-/remark-preset-lint-markdown-style-guide-5.1.3.tgz#69297ff80f15763c141d0ef4e151ad38deb116c4" + integrity sha512-4zNSPoiwAh4LJCbXh2U8Q9SFUIMw0MwsYJWTXHNiD0bGIUMWYU8ATLzDpWqCkzra6Ih7rLZuqB8tQIlipcM4Hg== + dependencies: + "@types/mdast" "^3.0.0" + remark-lint "^9.0.0" + remark-lint-blockquote-indentation "^3.0.0" + remark-lint-code-block-style "^3.0.0" + remark-lint-definition-case "^3.0.0" + remark-lint-definition-spacing "^3.0.0" + remark-lint-emphasis-marker "^3.0.0" + remark-lint-fenced-code-flag "^3.0.0" + remark-lint-fenced-code-marker "^3.0.0" + remark-lint-file-extension "^2.0.0" + remark-lint-final-definition "^3.0.0" + remark-lint-hard-break-spaces "^3.0.0" + remark-lint-heading-increment "^3.0.0" + remark-lint-heading-style "^3.0.0" + remark-lint-link-title-style "^3.0.0" + remark-lint-list-item-content-indent "^3.0.0" + remark-lint-list-item-indent "^3.0.0" + remark-lint-list-item-spacing "^4.0.0" + remark-lint-maximum-heading-length "^3.0.0" + remark-lint-maximum-line-length "^3.0.0" + remark-lint-no-blockquote-without-marker "^5.0.0" + remark-lint-no-consecutive-blank-lines "^4.0.0" + remark-lint-no-duplicate-headings "^3.0.0" + remark-lint-no-emphasis-as-heading "^3.0.0" + remark-lint-no-file-name-articles "^2.0.0" + remark-lint-no-file-name-consecutive-dashes "^2.0.0" + remark-lint-no-file-name-irregular-characters "^2.0.0" + remark-lint-no-file-name-mixed-case "^2.0.0" + remark-lint-no-file-name-outer-dashes "^2.0.0" + remark-lint-no-heading-punctuation "^3.0.0" + remark-lint-no-inline-padding "^4.0.0" + remark-lint-no-literal-urls "^3.0.0" + remark-lint-no-multiple-toplevel-headings "^3.0.0" + remark-lint-no-shell-dollars "^3.0.0" + remark-lint-no-shortcut-reference-image "^3.0.0" + remark-lint-no-shortcut-reference-link "^3.0.0" + remark-lint-no-table-indentation "^4.0.0" + remark-lint-ordered-list-marker-style "^3.0.0" + remark-lint-ordered-list-marker-value "^3.0.0" + remark-lint-rule-style "^3.0.0" + remark-lint-strong-marker "^3.0.0" + remark-lint-table-cell-padding "^4.0.0" + remark-lint-table-pipe-alignment "^3.0.0" + remark-lint-table-pipes "^4.0.0" + remark-lint-unordered-list-marker-style "^3.0.0" + unified "^10.0.0" + +remark-preset-lint-recommended@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/remark-preset-lint-recommended/-/remark-preset-lint-recommended-6.1.3.tgz#04b89a963bb7721d736fe731a9d8021d78ec95d7" + integrity sha512-DGjbeP2TsFmQeJflUiIvJWAOs1PxJt7SG3WQyMxOppkRr/up+mxWVkuv+6AUuaR0EsuaaFGz7WmZM5TrSSFWJw== + dependencies: + "@types/mdast" "^3.0.0" + remark-lint "^9.0.0" + remark-lint-final-newline "^2.0.0" + remark-lint-hard-break-spaces "^3.0.0" + remark-lint-list-item-bullet-indent "^4.0.0" + remark-lint-list-item-indent "^3.0.0" + remark-lint-no-blockquote-without-marker "^5.0.0" + remark-lint-no-duplicate-definitions "^3.0.0" + remark-lint-no-heading-content-indent "^4.0.0" + remark-lint-no-inline-padding "^4.0.0" + remark-lint-no-literal-urls "^3.0.0" + remark-lint-no-shortcut-reference-image "^3.0.0" + remark-lint-no-shortcut-reference-link "^3.0.0" + remark-lint-no-undefined-references "^4.0.0" + remark-lint-no-unused-definitions "^3.0.0" + remark-lint-ordered-list-marker-style "^3.0.0" + unified "^10.0.0" + +remark-preset-prettier@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/remark-preset-prettier/-/remark-preset-prettier-2.0.1.tgz#1b4469fd45b22c7d49e1b2d2594a8e7bba3cdac9" + integrity sha512-1+cZaM080zTlhQIvZDOj7SFWvW9zevfxsLjTZspLEsBYivMDwCyTodvNvaZLc9p5dUSMik/BjMFKicfhZXh7qg== + +remark-rehype@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279" + integrity sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw== + dependencies: + "@types/hast" "^2.0.0" + "@types/mdast" "^3.0.0" + mdast-util-to-hast "^12.1.0" + unified "^10.0.0" + +remark-stringify@^10.0.0: version "10.0.3" resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-10.0.3.tgz#83b43f2445c4ffbb35b606f967d121b2b6d69717" integrity sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A== @@ -10048,6 +11246,15 @@ remark-stringify@^10.0.0: mdast-util-to-markdown "^1.0.0" unified "^10.0.0" +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + remark@^14.0.2: version "14.0.3" resolved "https://registry.yarnpkg.com/remark/-/remark-14.0.3.tgz#e477886a7579df612908f387c7753dc93cdaa3fc" @@ -10058,6 +11265,16 @@ remark@^14.0.2: remark-stringify "^10.0.0" unified "^10.0.0" +remark@^15.0.0: + version "15.0.1" + resolved "https://registry.yarnpkg.com/remark/-/remark-15.0.1.tgz#ac7e7563260513b66426bc47f850e7aa5862c37c" + integrity sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A== + dependencies: + "@types/mdast" "^4.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.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" @@ -10780,6 +11997,15 @@ string-width@^5.0.0, string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" + integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^10.2.1" + strip-ansi "^7.0.1" + string.prototype.matchall@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" @@ -10853,7 +12079,7 @@ stringify-entities@^4.0.0: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: +strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== @@ -10950,6 +12176,11 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^9.0.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + supports-hyperlinks@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624" @@ -10976,6 +12207,14 @@ synckit@^0.8.6: "@pkgr/core" "^0.1.0" tslib "^2.6.2" +synckit@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.0.tgz#5b33b458b3775e4466a5b377fba69c63572ae449" + integrity sha512-7RnqIMq572L8PeEzKeBINYEJDDxpcH8JEgLwUqBd3TkofhFRbkq4QLR0u+36avGAhCRbk2nnmjcW9SE531hPDg== + dependencies: + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" + table@^6.8.1: version "6.8.1" resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" @@ -11314,6 +12553,11 @@ type-fest@^2.13.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +type-fest@^3.8.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + typed-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz#0608ffe6bca71bf15a45bff0ca2604107a1325f5" @@ -11361,6 +12605,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + typescript-json-schema@~0.52.0: version "0.52.0" resolved "https://registry.yarnpkg.com/typescript-json-schema/-/typescript-json-schema-0.52.0.tgz#954560ec90e5486e8f7a5b7706ec59286a708e29" @@ -11417,7 +12666,71 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -unified@^10.0.0: +unified-args@^11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/unified-args/-/unified-args-11.0.1.tgz#5c82564616288b8d99feed7326c2223097d30726" + integrity sha512-WEQghE91+0s3xPVs0YW6a5zUduNLjmANswX7YbBfksHNDGMjHxaWCql4SR7c9q0yov/XiIEdk6r/LqfPjaYGcw== + dependencies: + "@types/text-table" "^0.2.0" + chalk "^5.0.0" + chokidar "^3.0.0" + comma-separated-tokens "^2.0.0" + json5 "^2.0.0" + minimist "^1.0.0" + strip-ansi "^7.0.0" + text-table "^0.2.0" + unified-engine "^11.0.0" + +unified-engine@^11.0.0, unified-engine@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/unified-engine/-/unified-engine-11.2.0.tgz#bfd7296368a3b9cf7c36e1ab1d9db8327260a39f" + integrity sha512-H9wEDpBSM0cpEUuuYAOIiPzLCVN0pjASZZ6FFNzgzYS/HHzl9tArk/ereOMGtcF8m8vgjzw+HrU3YN7oenT7Ww== + dependencies: + "@types/concat-stream" "^2.0.0" + "@types/debug" "^4.0.0" + "@types/is-empty" "^1.0.0" + "@types/node" "^20.0.0" + "@types/unist" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + concat-stream "^2.0.0" + debug "^4.0.0" + glob "^10.0.0" + ignore "^5.0.0" + is-empty "^1.0.0" + is-plain-obj "^4.0.0" + load-plugin "^6.0.0" + parse-json "^7.0.0" + trough "^2.0.0" + unist-util-inspect "^8.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + vfile-reporter "^8.0.0" + vfile-statistics "^3.0.0" + yaml "^2.0.0" + +unified-lint-rule@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unified-lint-rule/-/unified-lint-rule-2.1.2.tgz#3ca2c6199b84aa8b48b60fda0f61b8eba55807d5" + integrity sha512-JWudPtRN7TLFHVLEVZ+Rm8FUb6kCAtHxEXFgBGDxRSdNMnGyTU5zyYvduHSF/liExlFB3vdFvsAHnNVE/UjAwA== + dependencies: + "@types/unist" "^2.0.0" + trough "^2.0.0" + unified "^10.0.0" + vfile "^5.0.0" + +unified-message-control@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unified-message-control/-/unified-message-control-4.0.0.tgz#7cd313df526fc660f218b19a56377bb6957019a8" + integrity sha512-1b92N+VkPHftOsvXNOtkJm4wHlr+UDmTBF2dUzepn40oy9NxanJ9xS1RwUBTjXJwqr2K0kMbEyv1Krdsho7+Iw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit "^3.0.0" + vfile "^5.0.0" + vfile-location "^4.0.0" + vfile-message "^3.0.0" + +unified@^10.0.0, unified@^10.1.0: version "10.1.2" resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== @@ -11430,7 +12743,7 @@ unified@^10.0.0: trough "^2.0.0" vfile "^5.0.0" -unified@^11.0.0: +unified@^11.0.0, unified@^11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.4.tgz#f4be0ac0fe4c88cb873687c07c64c49ed5969015" integrity sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ== @@ -11477,6 +12790,13 @@ unist-util-generated@^2.0.0: resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.1.tgz#e37c50af35d3ed185ac6ceacb6ca0afb28a85cae" integrity sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A== +unist-util-inspect@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/unist-util-inspect/-/unist-util-inspect-8.0.0.tgz#dcc6475bb7219ce410c6f3d03c7ab068cc2e351d" + integrity sha512-/3Wn/wU6/H6UEo4FoYUeo8KUePN8ERiZpQYFWYoihOsr1DoDuv80PeB0hobVZyYSvALa2e556bG1A1/AbwU4yg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" @@ -11503,6 +12823,13 @@ unist-util-position-from-estree@^1.0.0, unist-util-position-from-estree@^1.1.0: dependencies: "@types/unist" "^2.0.0" +unist-util-position-from-estree@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz#d94da4df596529d1faa3de506202f0c9a23f2200" + integrity sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-position@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-4.0.4.tgz#93f6d8c7d6b373d9b825844645877c127455f037" @@ -11562,6 +12889,14 @@ unist-util-visit-parents@^3.0.0: "@types/unist" "^2.0.0" unist-util-is "^4.0.0" +unist-util-visit-parents@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2" + integrity sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents@^5.0.0, unist-util-visit-parents@^5.1.1: version "5.1.3" resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" @@ -11587,6 +12922,15 @@ unist-util-visit@^2.0.3: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" +unist-util-visit@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-3.1.0.tgz#9420d285e1aee938c7d9acbafc8e160186dbaf7b" + integrity sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^4.0.0" + unist-util-visit@^4.0.0, unist-util-visit@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" @@ -11728,7 +13072,7 @@ uuid@^9.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== -uvu@^0.5.0: +uvu@^0.5.0, uvu@^0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== @@ -11770,6 +13114,14 @@ vfile-location@^3.2.0: resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== +vfile-location@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.1.0.tgz#69df82fb9ef0a38d0d02b90dd84620e120050dd0" + integrity sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw== + dependencies: + "@types/unist" "^2.0.0" + vfile "^5.0.0" + vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" @@ -11794,6 +13146,36 @@ vfile-message@^4.0.0: "@types/unist" "^3.0.0" unist-util-stringify-position "^4.0.0" +vfile-reporter@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-8.1.0.tgz#8d6057035c7133a1ea0da04c82bfef31c0756fa3" + integrity sha512-NfHyHdkCcy0BsXiLA3nId29TY7W7hgpc8nd8Soe3imATx5N4/+mkLYdMR+Y6Zvu6BXMMi0FZsD4FLCm1dN85Pg== + dependencies: + "@types/supports-color" "^8.0.0" + string-width "^6.0.0" + supports-color "^9.0.0" + unist-util-stringify-position "^4.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + vfile-sort "^4.0.0" + vfile-statistics "^3.0.0" + +vfile-sort@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vfile-sort/-/vfile-sort-4.0.0.tgz#fa1929065b62fe5311e5391c9434f745e8641703" + integrity sha512-lffPI1JrbHDTToJwcq0rl6rBmkjQmMuXkAxsZPRS9DXbaJQvc642eCg6EGxcX2i1L+esbuhq+2l9tBll5v8AeQ== + dependencies: + vfile "^6.0.0" + vfile-message "^4.0.0" + +vfile-statistics@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-3.0.0.tgz#0f5cd00c611c1862b13a9b5bc5599efaf465f2cf" + integrity sha512-/qlwqwWBWFOmpXujL/20P+Iuydil0rZZNglR+VNm6J0gpLHwuVM5s7g2TfVoswbXjZ4HuIhLMySEyIw5i7/D8w== + dependencies: + vfile "^6.0.0" + vfile-message "^4.0.0" + vfile@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" @@ -11814,7 +13196,7 @@ vfile@^5.0.0: unist-util-stringify-position "^3.0.0" vfile-message "^3.0.0" -vfile@^6.0.0: +vfile@^6.0.0, vfile@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.1.tgz#1e8327f41eac91947d4fe9d237a2dd9209762536" integrity sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw== @@ -11847,6 +13229,11 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" +walk-up-path@^3.0.1: + version "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.5: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -12171,7 +13558,7 @@ yaml@2.3.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== -yaml@^2.2.2: +yaml@^2.0.0, yaml@^2.2.2: version "2.3.4" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== From f72926f2fc9e159463dceb119774aeb4c68ebc18 Mon Sep 17 00:00:00 2001 From: josefaidt Date: Fri, 23 Feb 2024 16:19:28 -0800 Subject: [PATCH 16/91] rm stale setting --- .eslintrc.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index f4827915b4b..74fe66be10b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -31,10 +31,7 @@ "files": ["*.mdx"], "extends": ["plugin:mdx/recommended"], "settings": { - "mdx/code-blocks": true, - // optional, if you want to disable language mapper, set it to `false` - // if you want to override the default language mapper inside, you can provide your own - "mdx/language-mapper": {} + "mdx/code-blocks": true }, "rules": { // for mdx shortcodes, and snippets From b5cdee5e91cf93c392fb283c996a34465e4c98f8 Mon Sep 17 00:00:00 2001 From: John Corser Date: Mon, 26 Feb 2024 11:49:57 -0500 Subject: [PATCH 17/91] improve oauth docs (#6961) --- .../auth/add-social-provider/index.mdx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx b/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx index b2059ed7681..31e2c40bd58 100644 --- a/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx @@ -122,6 +122,7 @@ export const auth = defineAuth({ clientSecret: secret('GOOGLE_CLIENT_SECRET') }, signInWithApple: { + clientId: secret('SIWA_CLIENT_ID'), keyId: secret('SIWA_KEY_ID'), privateKey: secret('SIWA_PRIVATE_KEY'), teamId: secret('SIWA_TEAM_ID') @@ -139,6 +140,7 @@ export const auth = defineAuth({ 'https://mywebsite.com/profile' ], logoutUrls: ['http://localhost:3000/', 'https://mywebsite.com'], + // This required value will be prepended to `.auth.us-west-2.amazoncognito.com` and used for your application's oauth url domainPrefix: 'subdomain' } } @@ -287,15 +289,18 @@ If you are using the [Authenticator component](https://ui.docs.amplify.aws/react Enabling a signing in of a user with a social provider can be done as shown in the following code snippet. ```ts title="src/my-client-side-js.js" -import { Auth } from 'aws-amplify'; - -try { - const { user } = await Auth.signInWithWebUI({ - provider: 'google' - }); -} catch (error) { - console.log('error signing up:', error); -} +import { signInWithRedirect } from 'aws-amplify/auth'; +... + try { + await signInWithRedirect({ + provider: 'Apple' + }); + const currentUser = await getCurrentUser(); + const userAttributes = await fetchUserAttributes(); + console.log({currentUser, userAttributes}); + } catch (error) { + console.log('error signing up:', error); + } ``` ## Conclusion From 71b5756b68111580d7e68d74098f1aa9227b9ae0 Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:58:56 -0500 Subject: [PATCH 18/91] fixed gen 2 getting started guide (#6968) --- src/fragments/gen2/quickstart/create-amplify.mdx | 2 +- src/pages/gen2/build-a-backend/data/set-up-data/index.mdx | 2 +- .../fullstack-branching/mono-and-multi-repos/index.mdx | 2 +- src/pages/gen2/reference/project-structure/index.mdx | 2 +- src/pages/gen2/start/mobile-support/index.mdx | 6 +++--- src/pages/gen2/start/quickstart/vite-react-app/index.mdx | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fragments/gen2/quickstart/create-amplify.mdx b/src/fragments/gen2/quickstart/create-amplify.mdx index cfb0026015b..b6fefd17acd 100644 --- a/src/fragments/gen2/quickstart/create-amplify.mdx +++ b/src/fragments/gen2/quickstart/create-amplify.mdx @@ -2,7 +2,7 @@ The easiest way to get started with AWS Amplify is through npm with `create-amplify`. ```bash showLineNumbers={false} -npm create amplify@latest +npm create amplify@beta ``` ```console showLineNumbers={false} diff --git a/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx b/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx index 7793baf0088..d48432352eb 100644 --- a/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx @@ -25,7 +25,7 @@ With Amplify Data, you can build a secure, real-time API backed by a database in ## Building your data backend -If you've run `npm create amplify@latest` already, you should see an `amplify/data/resource.ts` file, which is the central location to configure your data backend. The most important element is the `schema` object, which defines your backend data models (`a.model()`) and custom queries (`a.query()`), mutations (`a.mutation()`), and subscriptions (`a.subscription()`). +If you've run `npm create amplify@beta` already, you should see an `amplify/data/resource.ts` file, which is the central location to configure your data backend. The most important element is the `schema` object, which defines your backend data models (`a.model()`) and custom queries (`a.query()`), mutations (`a.mutation()`), and subscriptions (`a.subscription()`). ```ts title="backend/data/resource.ts" // backend/data/resource.ts diff --git a/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx b/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx index 0da986d0506..d581894c164 100644 --- a/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx +++ b/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx @@ -17,7 +17,7 @@ You might have different frontend and backend teams that maintain their own repo ## Deploy the backend app -1. Run `mkdir backend-app && npm create amplify@latest` to set up a backend-only Amplify project. Commit the code to a Git provider of your choice. +1. Run `mkdir backend-app && npm create amplify@beta` to set up a backend-only Amplify project. Commit the code to a Git provider of your choice. 1. Connect the `backend-app` in the new console. Navigate to the Amplify console and select **New app > Build an app (Gen 2)**. diff --git a/src/pages/gen2/reference/project-structure/index.mdx b/src/pages/gen2/reference/project-structure/index.mdx index 61ef5dccc16..d71e4673a5c 100644 --- a/src/pages/gen2/reference/project-structure/index.mdx +++ b/src/pages/gen2/reference/project-structure/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { Amplify (Gen 2) backends are defined using TypeScript, and enable you to collocate resources depending on their function. For example, you can author a [post confirmation trigger for Amazon Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-post-confirmation.html) right next to your auth's resource file. -When you create your first Amplify project using `npm create amplify@latest`, it will automatically set up the scaffolding for Data and Authentication resources: +When you create your first Amplify project using `npm create amplify@beta`, it will automatically set up the scaffolding for Data and Authentication resources: ```text ├── amplify/ diff --git a/src/pages/gen2/start/mobile-support/index.mdx b/src/pages/gen2/start/mobile-support/index.mdx index c27d9659194..8d6b53f1c98 100644 --- a/src/pages/gen2/start/mobile-support/index.mdx +++ b/src/pages/gen2/start/mobile-support/index.mdx @@ -61,7 +61,7 @@ In **Select a Project Template**, select **Empty Activity** or **Empty Compose A The easiest way to get started with AWS Amplify is through npm with `create-amplify` command. You can run it from your base project directory. ```bash -npm create amplify +npm create amplify@beta ? Where should we create your project? (.) # press enter ``` @@ -498,7 +498,7 @@ For publishing the changes to cloud, you need to create a remote git repository. The easiest way to get started with AWS Amplify is through npm with `create-amplify` command. You can run it from your base project directory. ```bash -npm create amplify +npm create amplify@beta ? Where should we create your project? (.) # press enter ``` @@ -881,7 +881,7 @@ Now you should have your project created. The easiest way to get started with AWS Amplify is through npm with `create-amplify` command. You can run it from your base project directory. ```bash -npm create amplify +npm create amplify@beta ? Where should we create your project? (.) # press enter ``` diff --git a/src/pages/gen2/start/quickstart/vite-react-app/index.mdx b/src/pages/gen2/start/quickstart/vite-react-app/index.mdx index eaae7223129..bdb1b240573 100644 --- a/src/pages/gen2/start/quickstart/vite-react-app/index.mdx +++ b/src/pages/gen2/start/quickstart/vite-react-app/index.mdx @@ -37,7 +37,7 @@ npm install The easiest way to get started with AWS Amplify is through npm with `create-amplify`. ```bash title="Terminal" showLineNumbers={false} -npm create amplify@latest +npm create amplify@beta ``` ```console title="Terminal" showLineNumbers={false} From c97a89de3bd0230902179637c9f6c9d71761fa6d Mon Sep 17 00:00:00 2001 From: Heather Pundt <119376175+heatheramz@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:46:39 -0800 Subject: [PATCH 19/91] Update redirects for deleted hidden pages (#6944) --- redirects.json | 358 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 356 insertions(+), 2 deletions(-) diff --git a/redirects.json b/redirects.json index 200b0c0098e..89a1244a503 100644 --- a/redirects.json +++ b/redirects.json @@ -1697,6 +1697,36 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/auth/email-only-sign-up/", + "target":"/javascript/build-a-backend/auth/enable-sign-up/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/auth/email-only-sign-up/", + "target":"/angular/build-a-backend/auth/enable-sign-up/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/auth/email-only-sign-up/", + "target":"/nextjs/build-a-backend/auth/enable-sign-up/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/auth/email-only-sign-up/", + "target":"/react/build-a-backend/auth/enable-sign-up/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/auth/email-only-sign-up/", + "target":"/vue/build-a-backend/auth/enable-sign-up/", + "status":"301", + "condition":null + }, { "source":"/guides/authentication/listening-for-auth-events/q/platform/js/", "target":"/javascript/build-a-backend/auth/auth-events/", @@ -1709,6 +1739,36 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/auth/manage-user-attributes/", + "target":"/javascript/build-a-backend/auth/manage-user-profile/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/auth/manage-user-attributes/", + "target":"/angular/build-a-backend/auth/manage-user-profile/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/auth/manage-user-attributes/", + "target":"/nextjs/build-a-backend/auth/manage-user-profile/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/auth/manage-user-attributes/", + "target":"/react/build-a-backend/auth/manage-user-profile/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/auth/manage-user-attributes/", + "target":"/vue/build-a-backend/auth/manage-user-profile/", + "status":"301", + "condition":null + }, { "source":"/guides/datastore/parallel-processing/q/platform/ios/", "target":"/swift/prev/build-a-backend/more-features/datastore/", @@ -1733,6 +1793,48 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/javascript/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/swift/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, + { + "source":"/android/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/android/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/angular/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/nextjs/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/react/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/appsync-operations-to-lambda-layer/", + "target":"/vue/build-a-backend/graphqlapi/client-code-generation/", + "status":"301", + "condition":null + }, { "source":"/guides/functions/cognito-trigger-lambda-dynamodb/q/platform/android/", "target":"/android/build-a-backend/functions/", @@ -1751,6 +1853,48 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/javascript/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/swift/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/android/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/android/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/angular/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/nextjs/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/react/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/cognito-trigger-lambda-dynamodb/", + "target":"/vue/build-a-backend/functions/", + "status":"301", + "condition":null + }, { "source":"/guides/functions/connecting-a-rest-api/q/platform/ios/", "target":"/swift/build-a-backend/functions/", @@ -1763,6 +1907,48 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/functions/connect-rest-api/", + "target":"/javascript/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/connect-rest-api/", + "target":"/swift/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/android/build-a-backend/functions/connect-rest-api/", + "target":"/android/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/connect-rest-api/", + "target":"/angular/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/connect-rest-api/", + "target":"/nextjs/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/connect-rest-api/", + "target":"/react/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/connect-rest-api/", + "target":"/vue/build-a-backend/functions/", + "status":"301", + "condition":null + }, { "source":"/guides/functions/dynamodb-from-js-lambda/q/platform/android/", "target":"/android/build-a-backend/functions/", @@ -1781,6 +1967,48 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/javascript/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/swift/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/android/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/android/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/angular/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/nextjs/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/react/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/dynamodb-from-lambda-nodejs/", + "target":"/vue/build-a-backend/functions/", + "status":"301", + "condition":null + }, { "source":"/guides/functions/dynamodb-from-python-lambda/q/platform/android/", "target":"/android/build-a-backend/functions/", @@ -1800,14 +2028,56 @@ "condition":null }, { - "source":"/guides/functions/graphql-from-lambda/q/platform/ios/", + "source":"/javascript/build-a-backend/functions/dynamodb-from-lambda-python/", + "target":"/javascript/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/dynamodb-from-lambda-python/", "target":"/swift/build-a-backend/functions/", "status":"301", "condition":null }, + { + "source":"/android/build-a-backend/functions/dynamodb-from-lambda-python/", + "target":"/android/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/dynamodb-from-lambda-python/", + "target":"/angular/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/dynamodb-from-lambda-python/", + "target":"/nextjs/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/dynamodb-from-lambda-python/", + "target":"/react/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/dynamodb-from-lambda-python/", + "target":"/vue/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/guides/functions/graphql-from-lambda/q/platform/ios/", + "target":"/swift/build-a-backend/functions/graphql-from-lambda/", + "status":"301", + "condition":null + }, { "source":"/guides/functions/graphql-from-lambda/q/platform/js/", - "target":"/javascript/build-a-backend/functions/", + "target":"/javascript/build-a-backend/functions/graphql-from-lambda/", "status":"301", "condition":null }, @@ -1823,6 +2093,48 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/functions/graphql-server-lambda/", + "target":"/javascript/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/graphql-server-lambda/", + "target":"/swift/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/android/build-a-backend/functions/graphql-server-lambda/", + "target":"/android/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/graphql-server-lambda/", + "target":"/angular/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/graphql-server-lambda/", + "target":"/nextjs/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/graphql-server-lambda/", + "target":"/react/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/graphql-server-lambda/", + "target":"/vue/build-a-backend/functions/", + "status":"301", + "condition":null + }, { "source":"/guides/hosting/custom-domains/q/platform/js/", "target":"/javascript/deploy-and-host/custom-configuration/configure-custom-domain/", @@ -3227,6 +3539,48 @@ "status":"301", "condition":null }, + { + "source":"/javascript/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/javascript/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/swift/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/swift/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/android/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/android/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/angular/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/angular/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/nextjs/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/nextjs/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/react/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/react/build-a-backend/functions/", + "status":"301", + "condition":null + }, + { + "source":"/vue/build-a-backend/functions/integrate-dynamodb-with-lambda/", + "target":"/vue/build-a-backend/auth/enable-sign-up/", + "status":"301", + "condition":null + }, { "source":"/lib-v1/analytics/autotrack/q/platform/android/", "target":"/android/prev/build-a-backend/more-features/analytics/auto-track-sessions/", From 0b999108007eba841dcbef686925a2bf3442b5b2 Mon Sep 17 00:00:00 2001 From: Nikhil Swaminathan <2429410+swaminator@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:39:40 -0800 Subject: [PATCH 20/91] Update manual installation to point to @beta tag (#6972) --- src/pages/gen2/start/manual-installation/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/gen2/start/manual-installation/index.mdx b/src/pages/gen2/start/manual-installation/index.mdx index 574d6e3a4dd..a1653a342ac 100644 --- a/src/pages/gen2/start/manual-installation/index.mdx +++ b/src/pages/gen2/start/manual-installation/index.mdx @@ -19,7 +19,7 @@ The easiest way to get started with AWS Amplify is through [npm](https://npmjs.c First, if your frontend framework of choice doesn't have it already, create your project's `package.json` with `npm init -y`. Then, install the Amplify dependencies for building a backend: ```bash -npm add --save-dev @aws-amplify/backend @aws-amplify/backend-cli typescript +npm add --save-dev @aws-amplify/backend@beta @aws-amplify/backend-cli@beta typescript ``` @@ -99,7 +99,7 @@ defineBackend({ You can also update an existing frontend app. To upgrade existing Amplify code-first DX (Gen 2) apps, use your Node.js package manager (for example, `npm`) to update relevant backend packages: ```bash -npm update @aws-amplify/backend @aws-amplify/backend-cli +npm update @aws-amplify/backend@beta @aws-amplify/backend-cli@beta ``` ## Next steps From 74850e5377f58230a9d7576933fd828feef4aa22 Mon Sep 17 00:00:00 2001 From: John Corser Date: Tue, 27 Feb 2024 15:53:42 -0500 Subject: [PATCH 21/91] include usage for multi-page apps (#6964) * include usage for multi-page apps * improve multi-page app docs * use Hub syntax * update imports in examples --- .../auth/add-social-provider/index.mdx | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx b/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx index 31e2c40bd58..579376b824f 100644 --- a/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx @@ -295,14 +295,50 @@ import { signInWithRedirect } from 'aws-amplify/auth'; await signInWithRedirect({ provider: 'Apple' }); - const currentUser = await getCurrentUser(); - const userAttributes = await fetchUserAttributes(); - console.log({currentUser, userAttributes}); } catch (error) { console.log('error signing up:', error); } ``` +### (Required for Multi-Page Applications) Complete Social Sign In after Redirect + +If you are developing a multi-page application, and the redirected page is not the same page that initiated the sign in, you will need to add the following code to the redirected page to ensure the sign in gets completed: + +```ts title="src/my-redirected-page.js" +import 'aws-amplify/auth/enable-oauth-listener'; +import { getCurrentUser, fetchUserAttributes } from 'aws-amplify/auth'; +import { Hub } from 'aws-amplify/utils'; + +Hub.listen("auth", ({ payload }) => { + switch (payload.event) { + case "signInWithRedirect": + const user = await getCurrentUser(); + const userAttributes = await fetchUserAttributes(); + console.log({user, userAttributes}); + break; + case "signInWithRedirect_failure": + // handle sign in failure + break; + case "customOAuthState": + const state = payload.data; // this will be customState provided on signInWithRedirect function + console.log(state); + break; + } +}); +``` + + + +**NOTE:** The listener only works on the client side in the context of a SSR-enabled project, so ensure to import the listener on the client side only. For example, in a Next.js project, you should add the above import statement to a component that renders on the client side only by `'use client'`. + + + + + +When you import and use the `signInWithRedirect` function, it will add a listener as a side effect that will complete the social sign in when an end user is redirected back to your app. This works well in a single-page application but in a multi-page application, you might get redirected to a page that doesn't include the listener that was originally added as a side-effect. Hence you must include the specific OAuth listener on your login success page. + + + ## Conclusion Congratulations! You finished the **Add social provider sign-in** guide. In this guide, you learned how to set up your social auth provider with Amplify. Your users can now sign into your app using their social provider accounts. From 76619062ac1144accf215de7e817198b672db63a Mon Sep 17 00:00:00 2001 From: Jim Blanchard Date: Wed, 28 Feb 2024 14:47:20 -0600 Subject: [PATCH 22/91] chore: Clean up broken Amplify JS API reference links (#6976) * chore: Clean up broken Amplify JS API reference links. * Cleaned up language. --- src/fragments/lib-v1/auth/js/getting-started-rn.mdx | 2 -- src/fragments/lib-v1/datastore/js/examples-react-native.mdx | 4 ---- src/fragments/lib-v1/datastore/js/examples.mdx | 4 ---- src/fragments/lib-v1/utilities/cache.mdx | 4 ---- src/fragments/lib-v1/utilities/i18n.mdx | 4 ---- src/fragments/lib-v1/utilities/js/hub.mdx | 4 ---- src/fragments/lib-v1/utilities/serviceworker.mdx | 4 ---- src/fragments/lib/auth/js/advanced.mdx | 2 +- src/fragments/lib/auth/js/getting-started-rn.mdx | 2 +- src/fragments/lib/auth/js/getting-started.mdx | 2 +- src/fragments/lib/datastore/js/examples-react-native.mdx | 2 +- src/fragments/lib/datastore/js/examples.mdx | 2 +- src/fragments/start/getting-started/angular/auth.mdx | 2 +- src/fragments/start/getting-started/react/auth.mdx | 2 +- src/fragments/start/getting-started/reactnative/auth.mdx | 2 +- .../[platform]/build-a-backend/utilities/cache/index.mdx | 2 +- src/pages/[platform]/build-a-backend/utilities/i18n/index.mdx | 2 +- .../build-a-backend/utilities/service-worker/index.mdx | 2 +- .../prev/build-a-backend/auth/advanced-workflows/index.mdx | 4 ---- .../[platform]/tools/console/auth/user-management/index.mdx | 2 +- 20 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/fragments/lib-v1/auth/js/getting-started-rn.mdx b/src/fragments/lib-v1/auth/js/getting-started-rn.mdx index 387958ec832..ac6f3dd88bc 100644 --- a/src/fragments/lib-v1/auth/js/getting-started-rn.mdx +++ b/src/fragments/lib-v1/auth/js/getting-started-rn.mdx @@ -268,5 +268,3 @@ Follow the instructions in the [Sign in, Sign up and Sign out](/[platform]/build To implement authentication flows using Amplify you can either use the Amplify UI libraries or call authentication methods directly on the `Auth` class. `Auth` has over 30 methods including [`signUp`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-up), [`signIn`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-in), [`forgotPassword`](/[platform]/build-a-backend/auth/manage-user-session/#forgot-password), and [`signOut`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-out) that allow you full control over all aspects of the user authentication flow. - -Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html). diff --git a/src/fragments/lib-v1/datastore/js/examples-react-native.mdx b/src/fragments/lib-v1/datastore/js/examples-react-native.mdx index 1825a137b37..dc1ea80cd02 100644 --- a/src/fragments/lib-v1/datastore/js/examples-react-native.mdx +++ b/src/fragments/lib-v1/datastore/js/examples-react-native.mdx @@ -118,7 +118,3 @@ const styles = StyleSheet.create({ export default App; ``` - -## API Reference - -For the complete API documentation for DataStore, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/datastore.html) diff --git a/src/fragments/lib-v1/datastore/js/examples.mdx b/src/fragments/lib-v1/datastore/js/examples.mdx index 1a4487c4dad..640e0bec67f 100644 --- a/src/fragments/lib-v1/datastore/js/examples.mdx +++ b/src/fragments/lib-v1/datastore/js/examples.mdx @@ -184,7 +184,3 @@ export default { - -## API Reference - -For the complete API documentation for DataStore, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/datastore.html) diff --git a/src/fragments/lib-v1/utilities/cache.mdx b/src/fragments/lib-v1/utilities/cache.mdx index ab66c781056..7fed9e29d78 100644 --- a/src/fragments/lib-v1/utilities/cache.mdx +++ b/src/fragments/lib-v1/utilities/cache.mdx @@ -125,10 +125,6 @@ const newCache = Cache.createInstance(config); // Please provide a new keyPrefix which is the identifier of Cache. ``` -## API Reference - -For the complete API documentation for Cache module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/cacheobject.html) - ## Configuration ### Configuration Parameters diff --git a/src/fragments/lib-v1/utilities/i18n.mdx b/src/fragments/lib-v1/utilities/i18n.mdx index fe137417ace..433e6e75a45 100644 --- a/src/fragments/lib-v1/utilities/i18n.mdx +++ b/src/fragments/lib-v1/utilities/i18n.mdx @@ -46,7 +46,3 @@ Retrieves a phrase from the dictionary for the active language. If the phrase do ```javascript I18n.get('Sign In'); ``` - -### API Reference - -For the complete API documentation for i18n module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/i18n.html) diff --git a/src/fragments/lib-v1/utilities/js/hub.mdx b/src/fragments/lib-v1/utilities/js/hub.mdx index 42cc25e791c..7078a831945 100644 --- a/src/fragments/lib-v1/utilities/js/hub.mdx +++ b/src/fragments/lib-v1/utilities/js/hub.mdx @@ -208,7 +208,3 @@ class DogStatus extends Component { ``` Now when the store is updated the `` component re-renders on the screen. - -## API Reference - -For the complete API documentation for Hub module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/hubclass.html) diff --git a/src/fragments/lib-v1/utilities/serviceworker.mdx b/src/fragments/lib-v1/utilities/serviceworker.mdx index 41b37ca6c3f..22e7b771ece 100644 --- a/src/fragments/lib-v1/utilities/serviceworker.mdx +++ b/src/fragments/lib-v1/utilities/serviceworker.mdx @@ -124,10 +124,6 @@ If you enable AWS Amplify [Analytics](/[platform]/prev/build-a-backend/more-feat You can see those analytics events are related metrics in Amazon Pinpoint console. -## API Reference - -For the complete API documentation for ServiceWorker module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/) - ## Example Service Worker ```javascript diff --git a/src/fragments/lib/auth/js/advanced.mdx b/src/fragments/lib/auth/js/advanced.mdx index b4e40901601..220aca8871d 100644 --- a/src/fragments/lib/auth/js/advanced.mdx +++ b/src/fragments/lib/auth/js/advanced.mdx @@ -405,4 +405,4 @@ Amplify.configure(awsconfig, { ``` ## API reference -For the complete API documentation for Authentication module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html) +For the complete API documentation for Authentication module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html) diff --git a/src/fragments/lib/auth/js/getting-started-rn.mdx b/src/fragments/lib/auth/js/getting-started-rn.mdx index 42024ccc012..7715f7ad9a0 100644 --- a/src/fragments/lib/auth/js/getting-started-rn.mdx +++ b/src/fragments/lib/auth/js/getting-started-rn.mdx @@ -237,4 +237,4 @@ To implement authentication flows using Amplify you can either use the Amplify U `Auth` has over 30 methods including [`signUp`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-up), [`signIn`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-in), [`forgotPassword`](/[platform]/build-a-backend/auth/manage-user-session/#forgot-password), and [`signOut`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-out) that allow you full control over all aspects of the user authentication flow. -Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html). +Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html). diff --git a/src/fragments/lib/auth/js/getting-started.mdx b/src/fragments/lib/auth/js/getting-started.mdx index 7656208d64e..7f481f1ea16 100644 --- a/src/fragments/lib/auth/js/getting-started.mdx +++ b/src/fragments/lib/auth/js/getting-started.mdx @@ -62,4 +62,4 @@ To implement authentication flows using Amplify you can either use the Amplify U `Auth` has over 30 methods including [`signUp`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-up), [`signIn`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-in), [`forgotPassword`](/[platform]/build-a-backend/auth/manage-user-session/#forgot-password), and [`signOut`](/[platform]/build-a-backend/auth/enable-sign-up/#sign-out) that allow you full control over all aspects of the user authentication flow. -Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html). +Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html). diff --git a/src/fragments/lib/datastore/js/examples-react-native.mdx b/src/fragments/lib/datastore/js/examples-react-native.mdx index 2d913d1a083..08e233bea4c 100644 --- a/src/fragments/lib/datastore/js/examples-react-native.mdx +++ b/src/fragments/lib/datastore/js/examples-react-native.mdx @@ -122,4 +122,4 @@ export default App; ## API Reference -For the complete API documentation for DataStore, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/datastore.html) +For the complete API documentation for DataStore, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.datastore.html) diff --git a/src/fragments/lib/datastore/js/examples.mdx b/src/fragments/lib/datastore/js/examples.mdx index eb91c182bec..d760d0b5b20 100644 --- a/src/fragments/lib/datastore/js/examples.mdx +++ b/src/fragments/lib/datastore/js/examples.mdx @@ -188,4 +188,4 @@ export default { ## API Reference -For the complete API documentation for DataStore, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/datastore.html) +For the complete API documentation for DataStore, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.datastore.html) diff --git a/src/fragments/start/getting-started/angular/auth.mdx b/src/fragments/start/getting-started/angular/auth.mdx index fac33b8b2fd..f87cb7f4960 100644 --- a/src/fragments/start/getting-started/angular/auth.mdx +++ b/src/fragments/start/getting-started/angular/auth.mdx @@ -75,6 +75,6 @@ You can also [customize](https://ui.docs.amplify.aws/angular/components/authenti In addition to the `amplify-authenticator` you can build custom authentication flows using the `Auth` class. -`Auth` has over 30 methods including `signUp`, `signIn`, `forgotPassword`, and `signOut` that allow you full control over all aspects of the user authentication flow. Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html) +`Auth` has over 30 methods including `signUp`, `signIn`, `forgotPassword`, and `signOut` that allow you full control over all aspects of the user authentication flow. Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html) In the next section, you'll host your app on the **Amplify Console**, a hosting service complete with a globally available CDN, atomic deployments, easy custom domains, and CI / CD. diff --git a/src/fragments/start/getting-started/react/auth.mdx b/src/fragments/start/getting-started/react/auth.mdx index d7f4e587243..2eac4a8854f 100644 --- a/src/fragments/start/getting-started/react/auth.mdx +++ b/src/fragments/start/getting-started/react/auth.mdx @@ -286,6 +286,6 @@ In this example, you used the Amplify React UI library and the `withAuthenticato You can also [customize this component](https://ui.docs.amplify.aws/react/components/authenticator/customization) to add or remove fields, update styling, or other configurations. You can even [override function calls](https://ui.docs.amplify.aws/react/components/authenticator/customization#override-function-calls) if needed. -If you would like to build your own authentication flow instead, you can use the [Auth category in Amplify Libraries for JS](/[platform]/build-a-backend/auth/set-up-auth/). Amplify's Auth category class over 20 methods including `signUp`, `signIn`, `forgotPassword`, and `signOut` that allow you full control over all aspects of the user authentication flow. Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html). +If you would like to build your own authentication flow instead, you can use the [Auth category in Amplify Libraries for JS](/[platform]/build-a-backend/auth/set-up-auth/). Amplify's Auth category over 30 APIs including `signUp`, `signIn`, `forgotPassword`, and `signOut` that allow you full control over all aspects of the user authentication flow. Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html). In the next section, you'll host your app on the Amplify Console, a hosting service complete with a globally available CDN, automatic deployments, easy custom domains, and CI / CD. diff --git a/src/fragments/start/getting-started/reactnative/auth.mdx b/src/fragments/start/getting-started/reactnative/auth.mdx index 83ba3182fee..81c372bd6e2 100644 --- a/src/fragments/start/getting-started/reactnative/auth.mdx +++ b/src/fragments/start/getting-started/reactnative/auth.mdx @@ -386,4 +386,4 @@ In this example, you used the Amplify React UI library and the `withAuthenticato You can also [customize this component](https://ui.docs.amplify.aws/react-native/connected-components/authenticator/customization) to add or remove fields, update styling, or other configurations. You can even [override function calls](https://ui.docs.amplify.aws/react-native/connected-components/authenticator/customization#override-function-calls) if needed. -In addition to `withAuthenticator`, you can build [custom authentication flows with the Amplify Library for JS](/[platform]/build-a-backend/auth/set-up-auth/). Amplify's `Auth` class has over 30 methods including `signUp`, `signIn`, `forgotPassword`, and `signOut` that allow you full control over all aspects of the user authentication flow. Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html). +In addition to `withAuthenticator`, you can build [custom authentication flows with the Amplify Library for JS](/[platform]/build-a-backend/auth/set-up-auth/). Amplify's Auth category has over 30 APIs including `signUp`, `signIn`, `forgotPassword`, and `signOut` that allow you full control over all aspects of the user authentication flow. Check out the complete API [here](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html). diff --git a/src/pages/[platform]/build-a-backend/utilities/cache/index.mdx b/src/pages/[platform]/build-a-backend/utilities/cache/index.mdx index c4f2d598be0..4b19edeef81 100644 --- a/src/pages/[platform]/build-a-backend/utilities/cache/index.mdx +++ b/src/pages/[platform]/build-a-backend/utilities/cache/index.mdx @@ -184,7 +184,7 @@ const customCache = Cache.createInstance({ ``` ## API Reference -For the complete API documentation for Cache module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/interfaces/cache.html) +For the complete API documentation for Cache module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/aws_amplify.utils._Reference_Types_.StorageCache.html) ## Configuration diff --git a/src/pages/[platform]/build-a-backend/utilities/i18n/index.mdx b/src/pages/[platform]/build-a-backend/utilities/i18n/index.mdx index e0d8ba113c2..0b62786b6cf 100644 --- a/src/pages/[platform]/build-a-backend/utilities/i18n/index.mdx +++ b/src/pages/[platform]/build-a-backend/utilities/i18n/index.mdx @@ -90,6 +90,6 @@ I18n.get('Sign In'); ### API Reference -For the complete API documentation for i18n module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/i18n.html) +For the complete API documentation for i18n module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/aws_amplify.utils.I18n.html) diff --git a/src/pages/[platform]/build-a-backend/utilities/service-worker/index.mdx b/src/pages/[platform]/build-a-backend/utilities/service-worker/index.mdx index c8535f7325b..0330e70d651 100644 --- a/src/pages/[platform]/build-a-backend/utilities/service-worker/index.mdx +++ b/src/pages/[platform]/build-a-backend/utilities/service-worker/index.mdx @@ -168,7 +168,7 @@ You can see those analytics events are related metrics in Amazon Pinpoint consol ## API Reference -For the complete API documentation for ServiceWorker module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/serviceworkerclass.html) +For the complete API documentation for ServiceWorker module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/aws_amplify.utils.ServiceWorker.html) ## Example Service Worker diff --git a/src/pages/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx b/src/pages/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx index 846808f4329..1e66a9da23f 100644 --- a/src/pages/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx +++ b/src/pages/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx @@ -594,8 +594,4 @@ Note: To work with Service Interface Objects, your Amazon Cognito users' [IAM ro -## API reference - -For the complete API documentation for Authentication module, visit our [API Reference](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html) - diff --git a/src/pages/[platform]/tools/console/auth/user-management/index.mdx b/src/pages/[platform]/tools/console/auth/user-management/index.mdx index 4495e18f950..e70fbd59dd7 100644 --- a/src/pages/[platform]/tools/console/auth/user-management/index.mdx +++ b/src/pages/[platform]/tools/console/auth/user-management/index.mdx @@ -63,7 +63,7 @@ To set authorization rules that allow these users and groups to perform create, -A user can be confirmed by using the [pre-built UI components](/[platform]/build-a-backend/auth/set-up-auth/#enable-sign-up-sign-in-and-sign-out), [Amplify libraries](https://aws-amplify.github.io/amplify-js/api/classes/authclass.html) or [AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cognito-idp/confirm-sign-up.html) +A user can be confirmed by using the [pre-built UI components](/[platform]/build-a-backend/auth/set-up-auth/#enable-sign-up-sign-in-and-sign-out), [Amplify libraries](https://aws-amplify.github.io/amplify-js/api/modules/aws_amplify.auth.html) From 364735923d9e0695aa08554a0a83d32ac5af83e8 Mon Sep 17 00:00:00 2001 From: Jay Raval Date: Thu, 29 Feb 2024 12:33:09 -0500 Subject: [PATCH 23/91] Update build image instructions for Gen 2 (#6978) * update build image settings for Gen 2 * remove callout and add fragment to vite guide --- .../images/gen2/getting-started/console4.png | Bin 168480 -> 0 bytes .../gen2/quickstart/deploy-and-host.mdx | 10 +------ .../start/quickstart/vite-react-app/index.mdx | 27 ++---------------- 3 files changed, 3 insertions(+), 34 deletions(-) delete mode 100644 public/images/gen2/getting-started/console4.png diff --git a/public/images/gen2/getting-started/console4.png b/public/images/gen2/getting-started/console4.png deleted file mode 100644 index 8949b3268252422a0cd98aa0ed0627870024df68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168480 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 V`|sz+TsO%tX9xFFwRV9i{{@qO4=w-z diff --git a/src/fragments/gen2/quickstart/deploy-and-host.mdx b/src/fragments/gen2/quickstart/deploy-and-host.mdx index fe255d54024..e5b88dd18f4 100644 --- a/src/fragments/gen2/quickstart/deploy-and-host.mdx +++ b/src/fragments/gen2/quickstart/deploy-and-host.mdx @@ -15,15 +15,7 @@ If you already have your project remotely hosted in a Git repository, you can sk 3. In the Git provider screen, choose **Option 2: Start with an existing app**. Connect the repository you just deployed and pick a branch. -Next.js 14 apps require a [minimum node version](https://nextjs.org/blog/next-14#other-changes) of 18.17 at build time. - -4. Under **Advanced settings**, navigate to **Build image** and enter `amplify:al2023`. The Amplify Amazon Linux 2023 build image supports Node.js versions 18 and 20 at build time. You can specify the Node.js version during your build through [live package updates](https://docs.aws.amazon.com/amplify/latest/userguide/custom-build-image.html#setup-live-updates) or [nvm](https://github.com/nvm-sh/nvm). Amplify Hosting will automatically deploy your compute runtime to match the Node.js version used at build time. - -![Amplify Gen 2 console showing the "Advanced settings" view with "amplify:al2023" specified for build image.](/images/gen2/getting-started/console4.png) - -If you have an existing Gen-2 application, you can modify the build image by navigating to **Hosting > Environment variables** and creating a new environment variable `_CUSTOM_IMAGE` with the value `amplify:al2023`, then redeploying the build. - -5. Review all of your settings to ensure everything is set up correctly. Choose **Save and deploy** to deploy your web app. +4. Review all of your settings to ensure everything is set up correctly. Choose **Save and deploy** to deploy your web app. ### Manage fullstack branch diff --git a/src/pages/gen2/start/quickstart/vite-react-app/index.mdx b/src/pages/gen2/start/quickstart/vite-react-app/index.mdx index bdb1b240573..7f463c9fa52 100644 --- a/src/pages/gen2/start/quickstart/vite-react-app/index.mdx +++ b/src/pages/gen2/start/quickstart/vite-react-app/index.mdx @@ -240,30 +240,7 @@ useEffect(() => { Go to `localhost` in the browser to make sure you can now log in and create and list to-dos. You can end your development session by shutting down the frontend dev server and cloud sandbox. The sandbox prompts you to delete your backend resources. While you can retain your backend, we recommend deleting all resources so you can start clean again next time. -## Deploy and host a fullstack branch +import deployAndHost from 'src/fragments/gen2/quickstart/deploy-and-host.mdx'; -Now that your app is working, deploy it to a shared fullstack branch so you can share the project with your team. - -Amplify offers a fully managed hosting service with CI/CD built in, making it easy to set up production and staging environments with Git branches. In Gen 2, every Git branch in your repository maps 1:1 to a fullstack branch in Amplify. - -### Create remote Git repository - -If you already have your project remotely hosted in a Git repository, you can skip this step. Otherwise, navigate to your preferred Git provider, and add your project to a new repository. Amplify supports [GitHub](https://docs.github.com/en/get-started/quickstart/create-a-repo), [AWS CodeCommit](https://docs.aws.amazon.com/codecommit/latest/userguide/how-to-create-repository.html), [GitLab](https://docs.gitlab.com/ee/user/project/index.html), and [Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/create-a-git-repository/). - -### Connect branch in the Amplify console - -1. To get started with Gen 2, log in to the [AWS console](https://console.aws.amazon.com/console/home?#amplify) and navigate to your preferred AWS Region. (The Amplify console is available in [19 AWS Regions](https://docs.aws.amazon.com/general/latest/gr/amplify.html#amplify_region)). -2. From the **Public Preview** banner, choose **Try Amplify Gen 2**. - -![Amplify Gen 2 console showing the "Public Preview" banner with "Try Amplify Gen 2" button.](/images/gen2/getting-started/console1.png) - -3. In the Git provider screen, choose **Option 2: Start with an existing app**. Connect the repository you just deployed and pick a branch. - -If you have an existing Gen-2 app, you can modify the build image by navigating to **Hosting > Environment variables** and creating a new environment variable `_CUSTOM_IMAGE` with the value `amplify:al2023`, then redeploy the build. - -4. Review all of your settings to ensure everything is set up correctly. Choose **Save and deploy** to deploy your web app. - -### Manage fullstack branch - -The new Amplify console gives you a central place to manage your branches, hosting settings, CI/CD builds, and backend resources. Even though you can access backend resources directly from AWS service consoles, the Amplify console offers built-in user and data administration. + From fb1b8ed519961a9e3f8c6295e4d5a915bf51c2d6 Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:51:55 -0800 Subject: [PATCH 24/91] chore: add unit test for YoutubeEmbed component (#6983) * chore: add unit test for YoutubeEmbed component * add code coverage stats --------- Co-authored-by: katiegoines --- package.json | 2 +- .../__tests__/YoutubeEmbed.test.tsx | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/components/YoutubeEmbed/__tests__/YoutubeEmbed.test.tsx diff --git a/package.json b/package.json index 4922a142c58..07e1857edb8 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ "scripts": { "clean": "rm -rf node_modules yarn.lock", "refresh": "yarn clean && yarn", - "test": "jest", + "test": "jest --coverage", "dev": "yarn prebuild && next dev", "spellcheck": "cspell \"src/**/*.mdx\" --no-progress", "spellcheck-diff": "git diff --name-only --cached | awk \"/src.*\\.mdx/{print}\" | npx cspell --no-must-find-files --file-list stdin", diff --git a/src/components/YoutubeEmbed/__tests__/YoutubeEmbed.test.tsx b/src/components/YoutubeEmbed/__tests__/YoutubeEmbed.test.tsx new file mode 100644 index 00000000000..688cb896db1 --- /dev/null +++ b/src/components/YoutubeEmbed/__tests__/YoutubeEmbed.test.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { YoutubeEmbed } from '../index'; + +describe('YoutubeEmbed', () => { + const embedId = 'uaG2mMYLI68'; + const component = ; + it('should render the YoutubeEmbed component', async () => { + render(component); + const iframe = await screen.getByTitle('YouTube video player'); + expect(iframe).toBeInTheDocument(); + }); + + it('should be sized to 560w by 315h', async () => { + render(component); + const iframe = await screen.getByTitle('YouTube video player'); + expect(iframe.getAttribute('style')).toEqual( + '--youtube-embed-width: 560; --youtube-embed-height: 315;' + ); + }); +}); From f57a60a00f68d46ab44867be973edb672c1ca22d Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:52:05 -0800 Subject: [PATCH 25/91] chore: add unit tests for Accordion component (#6981) * chore: add unit tests for Accordion component * issue with window properties * fixed tracking test * working on animation issue * accordion tests * fix testing errors * added test for closing accordion * add code coverage stats --------- Co-authored-by: katiegoines --- .../Accordion/__tests__/Accordion.test.tsx | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/components/Accordion/__tests__/Accordion.test.tsx diff --git a/src/components/Accordion/__tests__/Accordion.test.tsx b/src/components/Accordion/__tests__/Accordion.test.tsx new file mode 100644 index 00000000000..3decf2377c5 --- /dev/null +++ b/src/components/Accordion/__tests__/Accordion.test.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { Accordion } from '../index'; +import * as trackModule from '../../../utils/track'; + +jest.mock('../../../utils/track', () => ({ + trackExpanderOpen: jest + .fn() + .mockImplementation(() => 'accordion-component-example') +})); + +HTMLElement.prototype.animate = jest.fn(); +window.scrollTo = jest.fn(); + +const content = + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla malesuada dignissim erat et lacinia. Quisque molestie vehicula dolor sit amet volutpat. Quisque eget orci quis mi sodales fringilla.'; +const title = 'Accordion component example'; +const headingLevel = '4'; +const eyebrow = 'Learn more'; +const component = ( + +

{content}

+
+); + +describe('Accordion', () => { + it('should render the Accordion component', async () => { + render(component); + const accordion = await screen.findByText(content); + expect(accordion).toBeInTheDocument(); + }); + + it('should render the title and eyebrow with correct classes', async () => { + render(component); + const t = await screen.getByText(title); + const e = await screen.getByText(eyebrow); + expect(t.className).toContain('accordion__heading'); + expect(e.className).toContain('accordion__eyebrow'); + }); + + it('should hide the Accordion body content on load', async () => { + render(component); + const bodyText = await screen.findByText(content); + expect(bodyText).not.toBeVisible(); + expect(bodyText).toBeInTheDocument(); + }); + + it('should expand Accordion when heading is clicked', async () => { + render(component); + const accordionHeading = screen.getByText('Accordion component example'); + userEvent.click(accordionHeading); + + await waitFor(() => { + expect(screen.getByText(content)).toBeInTheDocument(); + expect(screen.getByText(content)).toBeVisible(); + }); + }); + + it('should collapse Accordion when close button is clicked', async () => { + render(component); + await screen.getByText(content); + const closeButton = screen.getByRole('button'); + userEvent.click(closeButton); + + await waitFor(() => { + expect(screen.getByText(content)).not.toBeVisible(); + }); + }); + + it('should track Accordion open on click of heading', async () => { + jest.spyOn(trackModule, 'trackExpanderOpen'); + render(component); + const accordionHeading = screen.getByText('Accordion component example'); + userEvent.click(accordionHeading); + + await waitFor(() => { + expect(trackModule.trackExpanderOpen).toHaveBeenCalled(); + }); + }); +}); From 0a7e95bf7693310dc825d94fc978a1384c0bbf5c Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:25:40 -0500 Subject: [PATCH 26/91] Update index.mdx (#6986) --- src/pages/gen2/build-a-backend/data/mutate-data/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx b/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx index e15b02eec68..344e10312f3 100644 --- a/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx @@ -50,7 +50,7 @@ import { type Schema } from '@/amplify/data/resource' const client = generateClient(); -const todoDetails = { +const todo = { id: 'some_id', description: 'Updated description' }; From f9115e59ae76fae0caa306478db0c0a05f6b1571 Mon Sep 17 00:00:00 2001 From: Tim Nguyen <54393192+timngyn@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:17:46 -0800 Subject: [PATCH 27/91] Add env variables for algolia (#6957) * Add algolia env vars to next.config * Remove extra code string --- next.config.mjs | 3 +++ src/components/MDXComponents/MDXCode.tsx | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/next.config.mjs b/next.config.mjs index 29c78652869..26c81bcb7bd 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -24,6 +24,9 @@ const nextJSConfig = () => { distDir: 'client/www/next-build', env: { BUILD_ENV: process.env.BUILD_ENV, + ALGOLIA_APP_ID: process.env.ALGOLIA_APP_ID, + ALGOLIA_API_KEY: process.env.ALGOLIA_API_KEY, + ALGOLIA_INDEX_NAME: process.env.ALGOLIA_INDEX_NAME, nextImageExportOptimizer_imageFolderPath: 'public', nextImageExportOptimizer_exportFolderPath: 'out', nextImageExportOptimizer_quality: '75', diff --git a/src/components/MDXComponents/MDXCode.tsx b/src/components/MDXComponents/MDXCode.tsx index 1ee6f01bcad..f8a4f6256f2 100644 --- a/src/components/MDXComponents/MDXCode.tsx +++ b/src/components/MDXComponents/MDXCode.tsx @@ -62,10 +62,6 @@ export const MDXCode = ({ {({ style, tokens, getLineProps, getTokenProps }) => ( -
- {/* searchable code \ */} - {codeString} -
{shouldShowHeader ? ( From 7a9d346cd2ea13406fe08c0d294790f1dd3a5e5d Mon Sep 17 00:00:00 2001 From: David McAfee Date: Thu, 29 Feb 2024 12:27:20 -0800 Subject: [PATCH 28/91] fix(data): update REST API docs to document correct error response type (#6989) --- src/fragments/lib/restapi/js/delete.mdx | 4 ++-- src/fragments/lib/restapi/js/fetch.mdx | 8 ++++---- .../lib/restapi/js/getting-started/40_postTodo.mdx | 2 +- src/fragments/lib/restapi/js/update.mdx | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fragments/lib/restapi/js/delete.mdx b/src/fragments/lib/restapi/js/delete.mdx index c0e9fbc8378..da9ba59a09c 100644 --- a/src/fragments/lib/restapi/js/delete.mdx +++ b/src/fragments/lib/restapi/js/delete.mdx @@ -11,8 +11,8 @@ async function deleteTodo() { }); await restOperation.response; console.log('DELETE call succeeded'); - } catch (error) { - console.log('DELETE call failed: ', error); + } catch (e) { + console.log('DELETE call failed: ', JSON.parse(e.response.body)); } } ``` diff --git a/src/fragments/lib/restapi/js/fetch.mdx b/src/fragments/lib/restapi/js/fetch.mdx index ee906458497..e2614d68aa0 100644 --- a/src/fragments/lib/restapi/js/fetch.mdx +++ b/src/fragments/lib/restapi/js/fetch.mdx @@ -16,8 +16,8 @@ async function getTodo() { }); const response = await restOperation.response; console.log('GET call succeeded: ', response); - } catch (error) { - console.log('GET call failed: ', error); + } catch (e) { + console.log('GET call failed: ', JSON.parse(e.response.body)); } } ``` @@ -74,8 +74,8 @@ async function getTodo() { }); const response = await restOperation.response; console.log('GET call succeeded: ', response); - } catch (error) { - console.log('GET call failed: ', error); + } catch (e) { + console.log('GET call failed: ', JSON.parse(e.response.body)); } } ``` diff --git a/src/fragments/lib/restapi/js/getting-started/40_postTodo.mdx b/src/fragments/lib/restapi/js/getting-started/40_postTodo.mdx index 2dcbc4a629c..224b7f5c8c4 100644 --- a/src/fragments/lib/restapi/js/getting-started/40_postTodo.mdx +++ b/src/fragments/lib/restapi/js/getting-started/40_postTodo.mdx @@ -19,7 +19,7 @@ async function postTodo() { console.log('POST call succeeded'); console.log(response); } catch (e) { - console.log('POST call failed: ', e); + console.log('POST call failed: ', JSON.parse(e.response.body)); } } ``` diff --git a/src/fragments/lib/restapi/js/update.mdx b/src/fragments/lib/restapi/js/update.mdx index 042890e2ff1..bdd3cd8cd10 100644 --- a/src/fragments/lib/restapi/js/update.mdx +++ b/src/fragments/lib/restapi/js/update.mdx @@ -17,8 +17,8 @@ async function updateTodo() { }); const response = await restOperation.response; console.log('PUT call succeeded: ', response); - } catch (err) { - console.log('PUT call failed: ', err); + } catch (e) { + console.log('PUT call failed: ', JSON.parse(e.response.body)); } } ``` From b1480251c51c44a1c770929af24129c8369c93a8 Mon Sep 17 00:00:00 2001 From: David McAfee Date: Thu, 29 Feb 2024 12:36:47 -0800 Subject: [PATCH 29/91] fix(data): update React Native minimum iOS deployment target guidance; add build step to docs (#6970) * fix(data): update React Native minimum iOS deployment target; add build step to docs * address PR feedback * remove deployment target guidance * add React Native version deployment target callout * fix unrecognized syntax * replace callout with accordion * move 'upgrading' callout --- .../react-native-version-deployment-target.mdx | 10 ++++++++++ .../lib-v1/auth/js/getting-started-rn.mdx | 6 ++++++ .../getting-started/30_platformIntegration.mdx | 6 ++++++ .../js/getting-started-steps-basic-auth.mdx | 6 ++++++ src/fragments/lib/auth/js/getting-started-rn.mdx | 10 ++++++++++ ...ting-started-steps-basic-auth-react-native.mdx | 6 ++++++ .../getting-started/30_platformIntegration.mdx | 15 +++++++++------ .../react-native/install-dependencies.mdx | 4 ++++ .../lib/interactions/react-native/frontend.mdx | 4 ++++ .../getting_started/40_install_lib.mdx | 4 ++++ .../getting-started/20_reactnative_installLib.mdx | 4 ++++ .../lib/troubleshooting/common/upgrading.mdx | 11 ++++------- .../start/getting-started/reactnative/auth.mdx | 6 ++++++ .../reactnative/getting-started-steps-ui.mdx | 6 ++++++ .../start/getting-started/reactnative/setup.mdx | 11 ++++------- .../push-notifications-migration-guide/index.mdx | 4 ++++ .../start/getting-started/auth/index.mdx | 13 ++++++------- .../start/getting-started/setup/index.mdx | 15 +++++++++------ 18 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 src/fragments/common/react-native-version-deployment-target.mdx diff --git a/src/fragments/common/react-native-version-deployment-target.mdx b/src/fragments/common/react-native-version-deployment-target.mdx new file mode 100644 index 00000000000..0bdb14438a2 --- /dev/null +++ b/src/fragments/common/react-native-version-deployment-target.mdx @@ -0,0 +1,10 @@ + + + `@aws-amplify/react-native` requires a minimum iOS deployment target of `13.0` if you are using `react-native` version less than or equal to `0.72`. Open the _Podfile_ located in the _ios_ directory and update the `target` value: + + ```diff + - platform :ios, min_ios_version_supported + + platform :ios, 13.0 + ``` + + diff --git a/src/fragments/lib-v1/auth/js/getting-started-rn.mdx b/src/fragments/lib-v1/auth/js/getting-started-rn.mdx index ac6f3dd88bc..adadab24238 100644 --- a/src/fragments/lib-v1/auth/js/getting-started-rn.mdx +++ b/src/fragments/lib-v1/auth/js/getting-started-rn.mdx @@ -19,6 +19,12 @@ You will also need to install the pod dependencies for iOS: npx pod-install ``` +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` + diff --git a/src/fragments/lib-v1/datastore/react-native/getting-started/30_platformIntegration.mdx b/src/fragments/lib-v1/datastore/react-native/getting-started/30_platformIntegration.mdx index 03ec0ab49d1..68a38fb215d 100644 --- a/src/fragments/lib-v1/datastore/react-native/getting-started/30_platformIntegration.mdx +++ b/src/fragments/lib-v1/datastore/react-native/getting-started/30_platformIntegration.mdx @@ -21,6 +21,12 @@ You will also need to install the pod dependencies for iOS: npx pod-install ``` +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` + **Use SQLite for enhanced performance (optional)** React Native CLI apps can now use SQLite with DataStore. SQLite offers considerable performance advantages over the default "AsyncStorage" database. diff --git a/src/fragments/lib/auth/getting_started/js/getting-started-steps-basic-auth.mdx b/src/fragments/lib/auth/getting_started/js/getting-started-steps-basic-auth.mdx index 210436947ba..36df2083611 100644 --- a/src/fragments/lib/auth/getting_started/js/getting-started-steps-basic-auth.mdx +++ b/src/fragments/lib/auth/getting_started/js/getting-started-steps-basic-auth.mdx @@ -21,6 +21,12 @@ You will also need to install the pod dependencies for iOS: ```sh npx pod-install ``` + +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` diff --git a/src/fragments/lib/auth/js/getting-started-rn.mdx b/src/fragments/lib/auth/js/getting-started-rn.mdx index 7715f7ad9a0..97c1907f090 100644 --- a/src/fragments/lib/auth/js/getting-started-rn.mdx +++ b/src/fragments/lib/auth/js/getting-started-rn.mdx @@ -7,6 +7,10 @@ Amplify uses [Amazon Cognito](https://aws.amazon.com/cognito/) as the main authe +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + Install the necessary dependencies by running the following command: ```sh @@ -19,6 +23,12 @@ You will also need to install the pod dependencies for iOS: npx pod-install ``` +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` + diff --git a/src/fragments/lib/auth/js/getting-started-steps-basic-auth-react-native.mdx b/src/fragments/lib/auth/js/getting-started-steps-basic-auth-react-native.mdx index 59913dc4051..928e7fd4689 100644 --- a/src/fragments/lib/auth/js/getting-started-steps-basic-auth-react-native.mdx +++ b/src/fragments/lib/auth/js/getting-started-steps-basic-auth-react-native.mdx @@ -12,6 +12,12 @@ You will also need to install the pod dependencies for iOS: ```sh npx pod-install ``` + +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` diff --git a/src/fragments/lib/datastore/react-native/getting-started/30_platformIntegration.mdx b/src/fragments/lib/datastore/react-native/getting-started/30_platformIntegration.mdx index 66b89f82f89..3fb1537d34e 100644 --- a/src/fragments/lib/datastore/react-native/getting-started/30_platformIntegration.mdx +++ b/src/fragments/lib/datastore/react-native/getting-started/30_platformIntegration.mdx @@ -3,23 +3,26 @@ Start with the [React Native CLI](https://reactnative.dev/docs/getting-started): +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ```sh npx react-native init AmplifyDataStoreRN cd AmplifyDataStoreRN npm install aws-amplify @aws-amplify/react-native @react-native-community/netinfo @react-native-async-storage/async-storage react-native-get-random-values @azure/core-asynciterator-polyfill ``` -`@aws-amplify/react-native` requires a minimum iOS deployment target of `13.0`, open the _Podfile_ located in the _ios_ directory and update the `target` value: +You will also need to install the pod dependencies for iOS: -```diff -- platform :ios, min_ios_version_supported -+ platform :ios, 13.0 +```sh +npx pod-install ``` -You will also need to install the pod dependencies for iOS: +After installing pod dependencies, rebuild the app: ```sh -npx pod-install +npm run ios ``` **Use SQLite for enhanced performance (optional)** diff --git a/src/fragments/lib/in-app-messaging/integrate-your-application/react-native/install-dependencies.mdx b/src/fragments/lib/in-app-messaging/integrate-your-application/react-native/install-dependencies.mdx index f10c8b314fc..db8c2e07ef5 100644 --- a/src/fragments/lib/in-app-messaging/integrate-your-application/react-native/install-dependencies.mdx +++ b/src/fragments/lib/in-app-messaging/integrate-your-application/react-native/install-dependencies.mdx @@ -1,6 +1,10 @@ ### Install the Amplify React Native Package and other dependencies Installing the `@aws-amplify/react-native` will bring in the necessary polyfills. +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ```bash npm install @aws-amplify/react-native @react-native-community/netinfo @react-native-async-storage/async-storage ``` diff --git a/src/fragments/lib/interactions/react-native/frontend.mdx b/src/fragments/lib/interactions/react-native/frontend.mdx index 75b2d3a1089..b2790d4848a 100644 --- a/src/fragments/lib/interactions/react-native/frontend.mdx +++ b/src/fragments/lib/interactions/react-native/frontend.mdx @@ -2,6 +2,10 @@ import reactnative1 from '/src/fragments/lib/in-app-messaging/integrate-your-app ### Install Amplify and its dependencies +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ```bash npm install aws-amplify @aws-amplify/react-native @aws-amplify/interactions @react-native-community/netinfo @react-native-async-storage/async-storage react-native-get-random-values ``` diff --git a/src/fragments/lib/push-notifications/react-native/getting_started/40_install_lib.mdx b/src/fragments/lib/push-notifications/react-native/getting_started/40_install_lib.mdx index c0091217a89..8fa58e7d67b 100644 --- a/src/fragments/lib/push-notifications/react-native/getting_started/40_install_lib.mdx +++ b/src/fragments/lib/push-notifications/react-native/getting_started/40_install_lib.mdx @@ -1,5 +1,9 @@ In your project directory, you should first install the necessary dependencies for using Amplify Push Notifications. +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ```sh npm install aws-amplify @aws-amplify/react-native @aws-amplify/rtn-push-notification @react-native-async-storage/async-storage react-native-get-random-values ``` diff --git a/src/fragments/lib/restapi/js/getting-started/20_reactnative_installLib.mdx b/src/fragments/lib/restapi/js/getting-started/20_reactnative_installLib.mdx index 911649aafd2..2c8e5ae4b54 100644 --- a/src/fragments/lib/restapi/js/getting-started/20_reactnative_installLib.mdx +++ b/src/fragments/lib/restapi/js/getting-started/20_reactnative_installLib.mdx @@ -1,5 +1,9 @@ Use the package manager of your choice to install the amplify JS library. For example, with `npm`: +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ```bash npm install aws-amplify @aws-amplify/react-native ``` diff --git a/src/fragments/lib/troubleshooting/common/upgrading.mdx b/src/fragments/lib/troubleshooting/common/upgrading.mdx index 1447dc96a49..6bb8d66c1f6 100644 --- a/src/fragments/lib/troubleshooting/common/upgrading.mdx +++ b/src/fragments/lib/troubleshooting/common/upgrading.mdx @@ -53,6 +53,10 @@ We have introduced a new package - `@aws-amplify/react-native` - to encompass th > Native modules required by Amplify such as `@react-native-async-storage/async-storage` or `react-native-get-random-values` still need to be installed separately as they need to be linked with React Native +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ### Dropping support for Expo Go With the goal of providing more idiomatic native functionality to your React Native application, Expo Go will no longer be supported in v6. @@ -113,13 +117,6 @@ npm install aws-amplify@6 @aws-amplify/react-native @react-native-community/neti Note that v6 supports react-native v0.70+, so if you prefer manually upgrading dependencies double-check the version of react-native in your package.json file. -`@aws-amplify/react-native` requires a minimum iOS deployment target of `13.0`, if it has not already been set to `13.0` or above, open the _Podfile_ located in the _ios_ directory and update the `target` value: - -```diff -- platform :ios, min_ios_version_supported -+ platform :ios, 13.0 -``` - If you are using `signInWithRedirect` (previously `Auth.federatedSignIn`) you will need to install the `@aws-amplify/rtn-web-browser` native module. diff --git a/src/fragments/start/getting-started/reactnative/auth.mdx b/src/fragments/start/getting-started/reactnative/auth.mdx index 81c372bd6e2..28eb7f34389 100644 --- a/src/fragments/start/getting-started/reactnative/auth.mdx +++ b/src/fragments/start/getting-started/reactnative/auth.mdx @@ -61,6 +61,12 @@ You will also need to install the pod dependencies for iOS: npx pod-install ``` +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` + diff --git a/src/fragments/start/getting-started/reactnative/getting-started-steps-ui.mdx b/src/fragments/start/getting-started/reactnative/getting-started-steps-ui.mdx index 48b579bc182..e6969b44f68 100644 --- a/src/fragments/start/getting-started/reactnative/getting-started-steps-ui.mdx +++ b/src/fragments/start/getting-started/reactnative/getting-started-steps-ui.mdx @@ -21,5 +21,11 @@ You will also need to install the pod dependencies for iOS: ```sh npx pod-install ``` + +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` diff --git a/src/fragments/start/getting-started/reactnative/setup.mdx b/src/fragments/start/getting-started/reactnative/setup.mdx index e67f3693c8a..1fd1278330b 100644 --- a/src/fragments/start/getting-started/reactnative/setup.mdx +++ b/src/fragments/start/getting-started/reactnative/setup.mdx @@ -13,6 +13,10 @@ npx create-expo-app amplified_todo cd amplified_todo ``` +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + Install the necessary dependencies by running the following command: ```sh @@ -52,13 +56,6 @@ Install the necessary dependencies by running the following command: npm install aws-amplify @aws-amplify/react-native @react-native-community/netinfo @react-native-async-storage/async-storage react-native-get-random-values ``` -`@aws-amplify/react-native` requires a minimum iOS deployment target of `13.0`, open the _Podfile_ located in the _ios_ directory and update the `target` value: - -```diff -- platform :ios, min_ios_version_supported -+ platform :ios, 13.0 -``` - You will also need to install the pod dependencies for iOS: ```sh diff --git a/src/pages/[platform]/build-a-backend/push-notifications/push-notifications-migration-guide/index.mdx b/src/pages/[platform]/build-a-backend/push-notifications/push-notifications-migration-guide/index.mdx index b2cf735c650..6c44698fc62 100644 --- a/src/pages/[platform]/build-a-backend/push-notifications/push-notifications-migration-guide/index.mdx +++ b/src/pages/[platform]/build-a-backend/push-notifications/push-notifications-migration-guide/index.mdx @@ -25,6 +25,10 @@ This guide will help you migrate Amplify JavaScript v5's Push Notifications APIs With the introduction of v6, JavaScript polyfills and core native modules are a part of the `@aws-amplify/react-native` package. The necessary dependencies have changed as follows: +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + - `amazon-cognito-identity-js`, `@react-native-community/netinfo` are no longer required for Push Notifications. - `react-native-url-polyfill` no longer needs to be installed separately. - `@aws-amplify/react-native` is now required. diff --git a/src/pages/[platform]/start/getting-started/auth/index.mdx b/src/pages/[platform]/start/getting-started/auth/index.mdx index ea180e54c03..e17d7aead48 100644 --- a/src/pages/[platform]/start/getting-started/auth/index.mdx +++ b/src/pages/[platform]/start/getting-started/auth/index.mdx @@ -103,19 +103,18 @@ expo install @aws-amplify/ui-react-native react-native-safe-area-context npm install @aws-amplify/ui-react-native react-native-safe-area-context ``` -`@aws-amplify/react-native` requires a minimum iOS deployment target of `13.0`, open the _Podfile_ located in the _ios_ directory and update the `target` value: - -```diff -- platform :ios, min_ios_version_supported -+ platform :ios, 13.0 -``` - You will also need to install the pod dependencies for iOS: ```bash npx pod-install ``` +After installing pod dependencies, rebuild the app: + +```sh +npm run ios +``` + diff --git a/src/pages/[platform]/start/getting-started/setup/index.mdx b/src/pages/[platform]/start/getting-started/setup/index.mdx index 10c26cdef80..df5b2c82ee5 100644 --- a/src/pages/[platform]/start/getting-started/setup/index.mdx +++ b/src/pages/[platform]/start/getting-started/setup/index.mdx @@ -593,21 +593,24 @@ npm install aws-amplify @aws-amplify/react-native @react-native-community/netinf +import rnVersionCallout from '/src/fragments/common/react-native-version-deployment-target.mdx'; + + + ```sh npm install aws-amplify @aws-amplify/react-native @react-native-community/netinfo @react-native-async-storage/async-storage react-native-get-random-values ``` -`@aws-amplify/react-native` requires a minimum iOS deployment target of `13.0`, open the _Podfile_ located in the _ios_ directory and update the `target` value: +You will also need to install the pod dependencies for iOS: -```diff -- platform :ios, min_ios_version_supported -+ platform :ios, 13.0 +```sh +npx pod-install ``` -You will also need to install the pod dependencies for iOS: +After installing pod dependencies, rebuild the app: ```sh -npx pod-install +npm run ios ``` From d2d48fe4f61ec98ee249e62e14fc9eb02cc6429d Mon Sep 17 00:00:00 2001 From: Ankit Shah <22114629+ankpshah@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:20:58 -0800 Subject: [PATCH 30/91] Clarify behavior of forgetDevice() API regarding device tracking (#6985) * Clarify behavior of forgetDevice() API regarding device tracking This commit updates the documentation to clarify that using the forgetDevice() API results in the device being neither remembered nor tracked. Previously, the documentation suggested that forgotten devices might still be tracked, leading to confusion about the API's behavior. The revised wording aligns with the detailed description under the "Forgotten" section, ensuring consistency and eliminating ambiguity about how forgotten devices are handled. * Clarify behavior of forgetDevice() API regarding device tracking --- src/fragments/lib-v1/auth/common/device_features/common.mdx | 2 +- .../lib-v1/auth/native_common/device_features/common.mdx | 2 +- src/fragments/lib/auth/common/device_features/common.mdx | 2 +- src/fragments/lib/auth/native_common/device_features/common.mdx | 2 +- src/pages/[platform]/build-a-backend/auth/manage-mfa/index.mdx | 2 +- .../[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx | 2 +- .../prev/build-a-backend/auth/remember-device/index.mdx | 2 +- src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fragments/lib-v1/auth/common/device_features/common.mdx b/src/fragments/lib-v1/auth/common/device_features/common.mdx index 110715e32bc..576d81a3510 100644 --- a/src/fragments/lib-v1/auth/common/device_features/common.mdx +++ b/src/fragments/lib-v1/auth/common/device_features/common.mdx @@ -54,7 +54,7 @@ import flutter3 from '/src/fragments/lib-v1/auth/flutter/device_features/10_reme ### Forget Device -You can forget your device by using the following API. Note that forgotten devices are still tracked. See below for the difference between remembered, forgotten and tracked. +You can forget your device by using the following API. Note that forgotten devices are neither remembered nor tracked. See below for the difference between remembered, forgotten and tracked. import ios4 from '/src/fragments/lib-v1/auth/ios/device_features/20_forgetDevice.mdx'; diff --git a/src/fragments/lib-v1/auth/native_common/device_features/common.mdx b/src/fragments/lib-v1/auth/native_common/device_features/common.mdx index 01e5c8bfca6..9b3d2818b04 100644 --- a/src/fragments/lib-v1/auth/native_common/device_features/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/device_features/common.mdx @@ -50,7 +50,7 @@ import android1 from '/src/fragments/lib-v1/auth/android/device_features/10_reme ### Forget Device -You can forget your device by using the following API. Note that forgotten devices are still tracked. See below for the difference between remembered, forgotten and tracked. +You can forget your device by using the following API. Note that forgotten devices are neither remembered nor tracked. See below for the difference between remembered, forgotten and tracked. import ios2 from '/src/fragments/lib-v1/auth/ios/device_features/20_forgetDevice.mdx'; diff --git a/src/fragments/lib/auth/common/device_features/common.mdx b/src/fragments/lib/auth/common/device_features/common.mdx index 65b45b5de5a..034c635a0aa 100644 --- a/src/fragments/lib/auth/common/device_features/common.mdx +++ b/src/fragments/lib/auth/common/device_features/common.mdx @@ -77,7 +77,7 @@ import flutter3 from '/src/fragments/lib/auth/flutter/device_features/10_remembe ### Forget Device -You can forget your device by using the following API. Note that forgotten devices are still tracked. See below for the difference between remembered, forgotten and tracked. +You can forget your device by using the following API. Note that forgotten devices are neither remembered nor tracked. See below for the difference between remembered, forgotten and tracked. import ios4 from '/src/fragments/lib/auth/ios/device_features/20_forgetDevice.mdx'; diff --git a/src/fragments/lib/auth/native_common/device_features/common.mdx b/src/fragments/lib/auth/native_common/device_features/common.mdx index fda72dffeda..bf64c77f5b6 100644 --- a/src/fragments/lib/auth/native_common/device_features/common.mdx +++ b/src/fragments/lib/auth/native_common/device_features/common.mdx @@ -50,7 +50,7 @@ import android1 from '/src/fragments/lib/auth/android/device_features/10_remembe ### Forget Device -You can forget your device by using the following API. Note that forgotten devices are still tracked. See below for the difference between remembered, forgotten and tracked. +You can forget your device by using the following API. Note that forgotten devices are neither remembered nor tracked. See below for the difference between remembered, forgotten and tracked. import ios2 from '/src/fragments/lib/auth/ios/device_features/20_forgetDevice.mdx'; diff --git a/src/pages/[platform]/build-a-backend/auth/manage-mfa/index.mdx b/src/pages/[platform]/build-a-backend/auth/manage-mfa/index.mdx index 25d6c39dff2..9ef25edd7c3 100644 --- a/src/pages/[platform]/build-a-backend/auth/manage-mfa/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/manage-mfa/index.mdx @@ -849,7 +849,7 @@ export async function handleRememberDevice() { #### Forget devices -You can also forget devices but note that forgotten devices are still tracked. +You can also forget devices but note that forgotten devices are neither remembered nor tracked. diff --git a/src/pages/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx b/src/pages/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx index d52b4e7e0c6..3739686acc6 100644 --- a/src/pages/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx +++ b/src/pages/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx @@ -738,7 +738,7 @@ async function rememberDevice() { #### Forget devices -You can also forget devices but note that forgotten devices are still tracked. +You can also forget devices but note that forgotten devices are neither remembered nor tracked. diff --git a/src/pages/[platform]/prev/build-a-backend/auth/remember-device/index.mdx b/src/pages/[platform]/prev/build-a-backend/auth/remember-device/index.mdx index 902e692d5d0..39d55a1e762 100644 --- a/src/pages/[platform]/prev/build-a-backend/auth/remember-device/index.mdx +++ b/src/pages/[platform]/prev/build-a-backend/auth/remember-device/index.mdx @@ -125,7 +125,7 @@ import js2 from '/src/fragments/lib-v1/auth/js/device_features/10_rememberDevice ### Forget Device -You can forget your device by using the following API. Note that forgotten devices are still tracked. See below for the difference between remembered, forgotten and tracked. +You can forget your device by using the following API. Note that forgotten devices are neither remembered nor tracked. See below for the difference between remembered, forgotten and tracked. import js6 from '/src/fragments/lib-v1/auth/js/device_features/20_forgetDevice.mdx'; diff --git a/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx b/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx index 209bf9b0e8e..1d8d1741504 100644 --- a/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx @@ -701,7 +701,7 @@ export async function handleRememberDevice() { #### Forget devices -You can also forget devices but note that forgotten devices are still tracked. +You can also forget devices but note that forgotten devices are neither remembered nor tracked. From c641e6dca7635a89e9a13c11ecf5b65fb4c76b4a Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:00:50 -0500 Subject: [PATCH 31/91] Support for enum value listing (#6885) * Support for enum value listing * Update src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx --- .../data/data-modeling/add-fields/index.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx b/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx index 2b92ae6b8c6..a004782e1c2 100644 --- a/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx +++ b/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx @@ -126,6 +126,15 @@ client.models.Post.create({ }) ``` +### List enum values client-side + +You can list available enum values client-side using the `client.enums..values()` API. For example, this allows you to display the available enum values within a dropdown UI. + +```ts +const availableSettings = client.enums.PrivacySetting.values() +// availableSettings returns ["PRIVATE", "FRIENDS_ONLY", "PUBLIC"] +``` + ## Mark fields as required By default, fields are optional. To mark a field as required, use the `.required()` modifier. From e0c58b2cd29444ea148c59f5307e0f0fed385c2a Mon Sep 17 00:00:00 2001 From: Jim Eagan <84857865+hibler13@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:55:10 -0800 Subject: [PATCH 32/91] CDK updates v2 (#6990) --- .../existing-resources/cdk/index.mdx | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx b/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx index cec421861d8..d4330964cd8 100644 --- a/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx +++ b/src/pages/[platform]/build-a-backend/existing-resources/cdk/index.mdx @@ -2,7 +2,7 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Connect to existing AWS resources built with the CDK', - description: "Connect new mobile and web apps to AWS resources built with the CDK.", + description: "Connect a new app to AWS resources built with the CDK.", platforms: [ 'angular', 'flutter', @@ -27,22 +27,38 @@ export function getStaticProps(context) { }; } -This guide shows you how to connect new mobile and web apps to AWS resources you've already created using the [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/). The AWS CDK is an open-source software development framework for defining cloud infrastructure as code with modern programming languages. This infrastructure is then deployed through [AWS CloudFormation](https://aws.amazon.com/cloudformation/). +This guide shows you how to connect a new app to AWS resources you've already created using the [AWS Cloud Development Kit (AWS CDK)](https://aws.amazon.com/cdk/). The AWS CDK is an open-source software development framework for defining cloud infrastructure as code with modern programming languages. This infrastructure is then deployed through [AWS CloudFormation](https://aws.amazon.com/cloudformation/). -The guide has three sections to help you build and connect a fullstack app: + -1. [Section one](/[platform]/build-a-backend/existing-resources/cdk/#build-a-graphql-api-using-the-amplify-data-cdk-construct) provides instructions for using the Amplify Data CDK to create a GraphQL API backend with AWS AppSync. This creates the core backend. -1. [Section two](/[platform]/build-a-backend/existing-resources/cdk/#build-a-react-app-and-connect-to-the-graphql-api) explains how to create and connect a React web app to the GraphQL API. Follow this if you are using React to build a web frontend. -1. [Section three](/[platform]/build-a-backend/existing-resources/cdk/#build-a-flutter-app-and-connect-to-the-graphql-api) shows how to build and integrate a Flutter app with the GraphQL API. Follow this if you are using Flutter to build a cross-platform frontend. +In this guide, you will use the Amplify Data CDK to create a GraphQL API backend with AWS AppSync. This creates the core backend. You will then create and connect a React web app to the GraphQL API. -The recommended approach is to complete section one to build the backend, then optionally complete section two (React) or three (Flutter), or both, to connect frontends to the GraphQL API. Once you're finished experimenting with these demo apps, we recommend visiting the ["Clean up resources"](/[platform]/build-a-backend/existing-resources/cdk/#clean-up-resources) section to delete the backend resources to avoid incurring unexpected costs. + + + + +In this guide, you will use the Amplify Data CDK to create a GraphQL API backend with AWS AppSync. This creates the core backend. You will then build and integrate a Flutter app with the GraphQL API. + + Before you begin, you will need: + + * An AWS account: If you don't already have an account, follow the [Setting Up Your AWS Environment](https://aws.amazon.com/getting-started/guides/setup-environment/) tutorial for a quick overview. * The Amplify CLI [installed](https://docs.amplify.aws/cli/start/install/) and configured. * A text editor. For this guide, we will use VS Code, but you can use your preferred IDE. -* Flutter and its command-line tools [installed](https://docs.flutter.dev/get-started/install) and configured (if completing the third part of this guide). + + + + + +* An AWS account: If you don't already have an account, follow the [Setting Up Your AWS Environment](https://aws.amazon.com/getting-started/guides/setup-environment/) tutorial for a quick overview. +* The Amplify CLI [installed](https://docs.amplify.aws/cli/start/install/) and configured. +* A text editor. For this guide, we will use VS Code, but you can use your preferred IDE. +* Flutter and its command-line tools [installed](https://docs.flutter.dev/get-started/install) and configured. + + ## Build a GraphQL API using the Amplify Data CDK construct @@ -119,7 +135,9 @@ The CDK will deploy the stacks and display the following confirmation. Note the CDK deploying the stacks. -Now that you have built the backend API with the CDK, you can connect a frontend. Refer to section two to connect a React app or section three to connect a Flutter app, or complete both to connect frontends in each framework. +Now that you have built the backend API with the CDK, you can connect a frontend. + + ## Build a React app and connect to the GraphQL API @@ -306,9 +324,9 @@ npm start In this section, we generated GraphQL code, created React components, configured Amplify, and connected the app to the API. This enabled full CRUD functionality with our backend through queries and mutations. -## Conclusion - CDK and React + -Congratulations on successfully creating a new React app and linking it to a preexisting GraphQL API built with AWS CDK. The next optional section will guide you through doing the same for a Flutter mobile app. Alternatively, you can skip ahead to the ["Clean up resources"](/[platform]/build-a-backend/existing-resources/cdk/#clean-up-resources) section. + ## Build a Flutter app and connect to the GraphQL API @@ -808,13 +826,15 @@ flutter run -d chrome -## Conclusion - CDK and Flutter + + +## Conclusion -Congratulations! You used the AWS Amplify Data CDK construct to create a GraphQL API backend using AWS AppSync. You then connected either a Flutter app, a React app, or both to that API using the Amplify libraries. If you have any feedback, leave a [GitHub issue](https://github.com/aws-amplify/docs/issues) or join our [Discord Community](https://discord.gg/amplify)! +Congratulations! You used the AWS Amplify Data CDK construct to create a GraphQL API backend using AWS AppSync. You then connected your app to that API using the Amplify libraries. If you have any feedback, leave a [GitHub issue](https://github.com/aws-amplify/docs/issues) or join our [Discord Community](https://discord.gg/amplify)! ## Clean up resources -Once you're finished experimenting with these demo apps, we recommend deleting the backend resources to avoid incurring unexpected costs. You can do this by running the following command in the root folder of the CDK app created above. +Once you're finished experimenting with this demo app, we recommend deleting the backend resources to avoid incurring unexpected costs. You can do this by running the following command in the root folder of the CDK app created above. ```bash title="Terminal" showLineNumbers={false} cdk destroy From 3cbd591adaf909b2c2a5027df16d5edff734f4a2 Mon Sep 17 00:00:00 2001 From: Tim Nguyen <54393192+timngyn@users.noreply.github.com> Date: Fri, 1 Mar 2024 12:24:49 -0800 Subject: [PATCH 33/91] Add padding to bottom of nav menu (#6991) --- src/styles/menu.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/styles/menu.scss b/src/styles/menu.scss index 156b87d5e65..a1cea161304 100644 --- a/src/styles/menu.scss +++ b/src/styles/menu.scss @@ -1,4 +1,6 @@ .menu { + padding-bottom: var(--amplify-space-large); + &__list { list-style-type: none; margin: 0; From 8f43f839a2bf2cb0e618f532657adda4d5219515 Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:14:21 -0500 Subject: [PATCH 34/91] Adding secondary index to Gen 2 (#6996) * Adding secondary index docs * fixed some formatting for custom query fields --- src/directory/directory.mjs | 5 +- .../data/customize-authz/index.mdx | 2 +- .../data/data-modeling/index.mdx | 33 ++++- .../data-modeling/secondary-index/index.mdx | 138 ++++++++++++++++++ .../build-a-backend/data/query-data/index.mdx | 4 +- .../data/set-up-data/index.mdx | 4 +- .../data/subscribe-data/index.mdx | 6 +- 7 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 src/pages/gen2/build-a-backend/data/data-modeling/secondary-index/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index ea06a211cb6..2be06067343 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -2032,11 +2032,14 @@ export const directory = { { path: 'src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx' }, + { + path: 'src/pages/gen2/build-a-backend/data/data-modeling/relationships/index.mdx' + }, { path: 'src/pages/gen2/build-a-backend/data/data-modeling/identifiers/index.mdx' }, { - path: 'src/pages/gen2/build-a-backend/data/data-modeling/relationships/index.mdx' + path: 'src/pages/gen2/build-a-backend/data/data-modeling/secondary-index/index.mdx' } ] }, diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx index 770a1bb826f..2aaa4ecddf4 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx @@ -152,7 +152,7 @@ On the client side, make sure to always authenticate with the corresponding auth ```ts import { generateClient } from 'aws-amplify/data' -import type { Schema } from '@/backend/data/resource' // Path to your backend resource definition +import type { Schema } from '@/amplify/data/resource' // Path to your backend resource definition const client = generateClient() diff --git a/src/pages/gen2/build-a-backend/data/data-modeling/index.mdx b/src/pages/gen2/build-a-backend/data/data-modeling/index.mdx index 4dfdd2d69c3..69c70684b71 100644 --- a/src/pages/gen2/build-a-backend/data/data-modeling/index.mdx +++ b/src/pages/gen2/build-a-backend/data/data-modeling/index.mdx @@ -17,8 +17,37 @@ export function getStaticProps(context) { } -Every data model is defined as part of a data schema (`a.schema()`). Data models in Amplify Data are by-default backed by an Amazon DynamoDB table. +Every data model is defined as part of a data schema (`a.schema()`). You can enhance your data model with various fields, customize their identifiers, apply authorization rules, or model relationships. Every data model (`a.model()`) automatically provides create, read, update, and delete API operations as well as real-time subscription events. Below is a quick tour of the many functionalities you can add to your data model: -You can enhance your data model with various fields, customize their identifiers, apply authorization rules, or model relationships. Every data model automatically provides create, read, update, and delete API operations as well as real-time subscription events. +```ts +import { a } from '@aws-amplify/backend' + +const schema = a.schema({ + Customer: a.model({ + customerId: a.id().required(), + // fields can be of various scalar types, + // such as string, boolean, float, integers etc. + name: a.string(), + // fields can be of custom types + location: a.customType({ + // fields can be required or optional + lat: a.float().required(), + long: a.float().required() + }), + // fields can be enums + engagementStage: a.enum(["PROSPECT", "INTERESTED", "PURCHASED"]), + // Use custom identifiers. By default, it uses an `id: a.id()` field + }).identifier(["customerId"]), + + Collection: a.model({ + customers: a.hasMany('Customer'), // setup relationships between types + tags: a.string().array(), // fields can be arrays + representativeId: a.id().required() + // customize secondary indexes to optimize your query performance + }).secondaryIndexes([ + a.index('representativeId') + ]), +}).authorization([a.allow.owner()]) +``` diff --git a/src/pages/gen2/build-a-backend/data/data-modeling/secondary-index/index.mdx b/src/pages/gen2/build-a-backend/data/data-modeling/secondary-index/index.mdx new file mode 100644 index 00000000000..4bfd80abe21 --- /dev/null +++ b/src/pages/gen2/build-a-backend/data/data-modeling/secondary-index/index.mdx @@ -0,0 +1,138 @@ +export const meta = { + title: 'Customize secondary indexes', + description: + 'Define the secondary indexes for your data model to optimize query performance' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + +You can optimize your list queries based on "secondary indexes". For example, if you have a **Customer** model, you can query based on the customer's **id** identifier field by default but you can add a secondary index based on the **accountRepresentativeId** to get list customers for a given account representative. + +A secondary index consists of a "hash key" and, optionally, a "sort key". Use the "hash key" to perform strict equality and the "sort key" for greater than (gt), greater than or equal to (ge), less than (lt), less than or equal to (le), equals (eq), begins with, and between operations. + +```ts +export const schema = a.schema({ + Customer: a.model({ + name: a.string(), + phoneNumber: a.phone(), + accountRepresentativeId: a.id().required() + // highlight-start + }).secondaryIndexes([ + a.index('accountRepresentativeId') + ]) + // highlight-end +}) +``` + +The example client query below allows you to query for "Customer" records based on their `accountRepresentativeId`: + +```ts +import { type Schema } from '../amplify/data/resource' +import { generateClient } from 'aws-amplify/data' + +const client = generateClient() + +const { + data, + errors + // highlight-start +} = await client.models.Customer.listByAccountRepresentativeId({ + accountRepresentativeId: "YOUR_REP_ID" +}) + // highlight-end +``` + + + +Amplify uses Amazon DynamoDB tables as the default data source for `a.model()`. For key-value databases, it is critical to model your access patterns with "secondary indexes". Use the `.index()` modifier to configure a secondary index. + +**Amazon DynamoDB** is a key-value and document database that delivers single-digit millisecond performance at any scale but making it work for your access patterns requires a bit of forethought. DynamoDB query operations may use at most two attributes to efficiently query data. The first query argument passed to a query (the hash key) must use strict equality and the second attribute (the sort key) may use gt, ge, lt, le, eq, beginsWith, and between. DynamoDB can effectively implement a wide variety of access patterns that are powerful enough for the majority of applications. + + + +## Add sort keys to secondary indexes + +You can define "sort keys" to add a set of flexible filters to your query, such as "greater than" (gt), "greater than or equal to" (ge), "less than" (lt), "less than or equal to" (le), "equals" (eq), "begins with" (beginsWith), and "between" operations. + +```ts title="amplify/data/resource.ts" +const schema = a.schema({ + Customer: a.model({ + name: a.string(), + phoneNumber: a.phone().required(), + accountRepresentativeId: a.id().required() + }).secondaryIndexes([ + a.index('accountRepresentativeId') + // highlight-next-line + .sortKeys(["name"]) + ]) +}).authorization([a.allow.owner()]) +``` + +On the client side, you should find a new `listBy...` query that's named after hash key and sort keys. For example, in this case: `listByAccountRepresentativeIdAndName`. You can supply the filter as part of this new list query: + +```ts +const { + data, + errors + // highlight-next-line +} = await client.models.Customer.listByAccountRepresentativeIdAndName({ + accountRepresentativeId: "YOUR_REP_ID", + name: { + beginsWith: "Rene" + } +}) +``` + +## Customize the query field for secondary indexes + +You can also customize the auto-generated query name under `client.models..listBy...` by setting the `queryField()` modifier. + +```ts title="amplify/data/resource.ts" +const schema = a.schema({ + Customer: a.model({ + name: a.string(), + phoneNumber: a.phone(), + accountRepresentativeId: a.id().required() + }).secondaryIndexes([ + a.index('accountRepresentativeId') + // highlight-next-line + .queryField("listByRep") + ]) +}).authorization([a.allow.owner()]) +``` + +In your client app code, you'll see query updated under the Data client: + +```ts +const { + data, + errors + // highlight-next-line +} = await client.models.Customer.listByRep({ + accountRepresentativeId: "YOUR_REP_ID" +}) +``` + +## Customize the name of secondary indexes + +To customize the underlying DynamoDB's index name, you can optionally provide the `name()` modifier. + +```ts +const schema = a.schema({ + Customer: a.model({ + name: a.string(), + phoneNumber: a.phone(), + accountRepresentativeId: a.id().required() + }).secondaryIndexes([ + a.index('accountRepresentativeId') + // highlight-next-line + .name("MyCustomIndexName") + ]) +}).authorization([a.allow.owner()]) +``` diff --git a/src/pages/gen2/build-a-backend/data/query-data/index.mdx b/src/pages/gen2/build-a-backend/data/query-data/index.mdx index 5c63c216096..94f6c84f1d0 100644 --- a/src/pages/gen2/build-a-backend/data/query-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/query-data/index.mdx @@ -203,7 +203,7 @@ const { data: blogWithSubsetOfData, errors } = await client.models.Blog.get( When using TypeScript, you frequently need to specify data model types for type generics. For instance, with React's `useState`, you provide a type in TypeScript to ensure type-safety in your component code using the state. Use the `Schema["MODEL_NAME"]` pattern to get TypeScript types for the shapes of data models returned from the backend API. This allows you to get consumable TypeScript types for the shapes of the data model return values coming from the backend API. ```ts -import { type Schema } from '@/backend/data/resource'; +import { type Schema } from '@/amplify/data/resource'; type Post = Schema['Post']; @@ -214,7 +214,7 @@ You can combine the `Schema["MODEL_NAME"]` type with the `SelectionSet` helper t ```ts import { type SelectionSet } from 'aws-amplify/data' -import { type Schema } from '@/backend/data/resource' +import { type Schema } from '@/amplify/data/resource' const selectionSet = ['content', 'author', 'comments.*'] as const; diff --git a/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx b/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx index d48432352eb..f3e9e32e016 100644 --- a/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx @@ -27,8 +27,8 @@ With Amplify Data, you can build a secure, real-time API backed by a database in If you've run `npm create amplify@beta` already, you should see an `amplify/data/resource.ts` file, which is the central location to configure your data backend. The most important element is the `schema` object, which defines your backend data models (`a.model()`) and custom queries (`a.query()`), mutations (`a.mutation()`), and subscriptions (`a.subscription()`). -```ts title="backend/data/resource.ts" -// backend/data/resource.ts +```ts title="amplify/data/resource.ts" +// amplify/data/resource.ts import { a, defineData, type ClientSchema } from '@aws-amplify/backend'; diff --git a/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx b/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx index 26e99a477f8..c9e61444339 100644 --- a/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx @@ -26,7 +26,7 @@ The recommended way to fetch a list of data is to use `observeQuery` to get a re ```ts import { useState, useEffect } from 'react' import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/backend/data/resource' +import { type Schema } from '@/amplify/data/resource' type Todo = Schema["Todo"] @@ -59,7 +59,7 @@ Subscriptions is a feature that allows the server to send data to its clients wh ```ts import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/backend/data/resource'; +import { type Schema } from '@/amplify/data/resource'; const client = generateClient(); @@ -93,7 +93,7 @@ Subscriptions take an optional `filter` argument to define service-side subscrip ```ts import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/backend/data/resource'; +import { type Schema } from '@/amplify/data/resource'; const client = generateClient(); From 8adadb209683caf64ca8043b05bd6da006aa7081 Mon Sep 17 00:00:00 2001 From: Kethan sai Date: Mon, 4 Mar 2024 14:26:16 -0500 Subject: [PATCH 35/91] extend config to show addOuput (#6963) * extend config * modify wording * modify example * improve wording --- .../reference/amplifyconfiguration/index.mdx | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/pages/gen2/reference/amplifyconfiguration/index.mdx b/src/pages/gen2/reference/amplifyconfiguration/index.mdx index e05d5938124..fd52b799255 100644 --- a/src/pages/gen2/reference/amplifyconfiguration/index.mdx +++ b/src/pages/gen2/reference/amplifyconfiguration/index.mdx @@ -16,3 +16,90 @@ In Amplify (Gen 2), the CLI will generate an `amplifyconfiguration.json` file wi You can also manually create this file for a specified Amplify app ID and branch, or an AWS CloudFormation stack name with [`amplify generate config`](/gen2/reference/cli-commands#amplify-generate-config). {/* @TODO full type/interface to reference */} + +## Extending Amplify configuration file + +The `amplifyconfiguration.json` file is not just a static artifact; it's designed to be extendable to suit the evolving needs of your application. By leveraging the `addOutput` method from your `backend`, you can programmatically add configurations. This is particularly useful for customizing outputs that are not directly exposed through the Amplify constructs or for dynamically adjusting your app's configuration in response to changes in your backend strategy. + + + +Overriding Amplify-managed configurations on `amplifyconfiguration.json` is not supported. + + + +One common scenario where extending the configuration becomes handy is when you need to add custom outputs or extend existing configurations without manual file edits. + +Consider a scenario where you want to add output parameters in your `amplifyconfiguration.json` that specify an S3 bucket and its region that your application will use for storing files. + +```ts title="amplify/backend.ts" +import { defineBackend } from "@aws-amplify/backend"; +import { auth } from "./auth/resource"; +import { data } from "./data/resource"; + +const backend = defineBackend({ + auth, + data, +}); + +backend.addOutput({ + aws_user_files_s3_bucket: "my-externally-managed-bucket", + aws_user_files_s3_bucket_region: "us-east-1", +}); +``` + +In your frontend end application, you can configure Amplify as follows: + +```ts title="src/index.ts" +import { Amplify } from "aws-amplify"; +import config from "@/amplifyconfiguration.json"; + +Amplify.configure(config); +``` + +### Custom configuration + +In addition to extending existing configurations, you can also add custom output parameters to your `amplifyconfiguration.json`. This is useful for surfacing arbitrary outputs, values from custom CDK resources, or any other information that might be necessary for your application's logic or configuration. + +```ts title="amplify/backend.ts" +import { defineBackend } from "@aws-amplify/backend"; +import { auth } from "./auth/resource"; +import { data } from "./data/resource"; + +const backend = defineBackend({ + auth, + data, +}); + +backend.addOutput({ + custom: { + apiId: "restAPIId", + apiEndpoint: "https://api.example.com", + apiName: "restApiName", + }, +}); +``` + + +In your frontend end application, you can access these custom configurations as follows: + +```ts title="src/index.ts" +import { Amplify } from "aws-amplify"; +import config from "@/amplifyconfiguration.json"; + +Amplify.configure(config); +const currentConfig = Amplify.getConfig(); +Amplify.configure({ + ...currentConfig, + API: { + REST: { + [config.custom.apiName]: { + endpoint: config.custom.apiEndpoint, + region: "us-east-1", + }, + }, + }, +}); +``` + +{/* @TODO Link examples once published*/} + From 6a60ffbe9ee69653657fd9ebac42c6508a933d6e Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:00:27 -0500 Subject: [PATCH 36/91] Update index.mdx (#6995) * Update index.mdx * add guest access support to docs --- .../[platform]/build-a-backend/auth/set-up-auth/index.mdx | 2 ++ .../build-a-backend/graphqlapi/connect-to-api/index.mdx | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx b/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx index 3568474b1b1..d914369cad9 100644 --- a/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx @@ -251,6 +251,8 @@ Amplify.configure({ userPoolClientId: 'a1b2c3d4e5f6g7h8i9j0k1l2m3', // REQUIRED only for Federated Authentication - Amazon Cognito Identity Pool ID identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab', + // OPTIONAL - Set to true to use your identity pool's unauthenticated role when user is not logged in + allowGuestAccess: true // OPTIONAL - This is used when autoSignIn is enabled for Auth.signUp // 'code' is used for Auth.confirmSignUp, 'link' is used for email link verification signUpVerificationMethod: 'code', // 'code' | 'link' diff --git a/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx b/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx index 521f60fbdba..0f57d393859 100644 --- a/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx +++ b/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx @@ -167,6 +167,12 @@ Amplify.configure({ // Set the default auth mode to "iam" defaultAuthMode: 'iam' } + }, + Auth: { + Cognito: { + identityPoolId: "YOUR_IDENTITY_POOL_ID", + allowGuestAccess: true // set to false to not use the unauthenticated role + } } }); ``` From 5c3f5498f39f9c79172ff2ad31fa1a8db3c649dd Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:27:28 -0800 Subject: [PATCH 37/91] chore: add unit tests for NextPrevious component (#7003) Co-authored-by: katiegoines --- .../__tests__/NextPrevious.test.tsx | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/components/NextPrevious/__tests__/NextPrevious.test.tsx diff --git a/src/components/NextPrevious/__tests__/NextPrevious.test.tsx b/src/components/NextPrevious/__tests__/NextPrevious.test.tsx new file mode 100644 index 00000000000..fc68bb6bc50 --- /dev/null +++ b/src/components/NextPrevious/__tests__/NextPrevious.test.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { NextPrevious } from '../index'; + +const routerMock = { + __esModule: true, + useRouter: () => { + return { + query: { platform: 'react' }, + pathname: '/[platform]/build-a-backend/auth/manage-user-session' + }; + } +}; + +jest.mock('next/router', () => routerMock); + +describe('NextPrevious', () => { + it('should render the NextPrevious component', async () => { + render(); + const nextPrevNode = await screen.findByText('NEXT'); + expect(nextPrevNode).toBeInTheDocument(); + }); + + it('should include href to next/prev pages', async () => { + render(); + const nextPrevNode = await screen.findAllByRole('link'); + expect(nextPrevNode[0].textContent).toContain('PREVIOUS'); + expect(nextPrevNode[0].href).toContain( + '/react/build-a-backend/auth/set-up-auth' + ); + expect(nextPrevNode[1].textContent).toContain('NEXT'); + expect(nextPrevNode[1].href).toContain( + '/react/build-a-backend/auth/enable-sign-up' + ); + }); +}); From 98893304b8e7cb0f46f9f3e05107aa96bae85c3d Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:06:12 -0800 Subject: [PATCH 38/91] chore: add unit tests for LinkCards and LinkCard components (#7006) Co-authored-by: katiegoines --- .../LinkCard/__tests__/LinkCard.test.tsx | 29 ++++++++++++++++ .../LinkCards/__tests__/LinkCards.test.tsx | 34 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/components/LinkCard/__tests__/LinkCard.test.tsx create mode 100644 src/components/LinkCards/__tests__/LinkCards.test.tsx diff --git a/src/components/LinkCard/__tests__/LinkCard.test.tsx b/src/components/LinkCard/__tests__/LinkCard.test.tsx new file mode 100644 index 00000000000..6aa1c68f793 --- /dev/null +++ b/src/components/LinkCard/__tests__/LinkCard.test.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import LinkCard from '../index'; +import { IconGithub } from '@/components/Icons'; + +describe('LinkCard', () => { + const child =
Child Content
; + const component = ( + } + > + {child} + + ); + + it('should render the LinkCard component', async () => { + render(component); + const linkCardNode = await screen.findByText('Child Content'); + expect(linkCardNode).toBeInTheDocument(); + }); + + it('should link to href', async () => { + render(component); + const linkCardNode = await screen.findByRole('link'); + expect(linkCardNode.href).toBe('https://github.com/aws-amplify/amplify-ui'); + }); +}); diff --git a/src/components/LinkCards/__tests__/LinkCards.test.tsx b/src/components/LinkCards/__tests__/LinkCards.test.tsx new file mode 100644 index 00000000000..e6edf6ba7e2 --- /dev/null +++ b/src/components/LinkCards/__tests__/LinkCards.test.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import LinkCards from '../index'; + +describe('LinkCards', () => { + const component = ; + it('should render the LinkCards component', async () => { + render(component); + const linkCardNode = await screen.findByRole('link', { + name: 'React Libraries on GitHub' + }); + expect(linkCardNode).toBeInTheDocument(); + }); + + it('should link each card to external href', async () => { + render(component); + const githubCard = await screen.findByRole('link', { + name: 'React Libraries on GitHub' + }); + const discordCard = await screen.findByRole('link', { + name: 'Amplify Discord' + }); + const learnCard = await screen.findByRole('link', { + name: 'Amplify Learn' + }); + + expect(githubCard.href).toBe('https://github.com/aws-amplify/amplify-ui'); + expect(githubCard.target).toBe('_blank'); + expect(discordCard.href).toBe('https://discord.gg/amplify'); + expect(discordCard.target).toBe('_blank'); + expect(learnCard.href).toBe('https://amplify.aws/learn'); + expect(learnCard.target).toBe('_blank'); + }); +}); From 0209b3b0edd67223f56641394fc0a76394e3d726 Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:07:05 -0800 Subject: [PATCH 39/91] chore: add unit tests for GetStartedPopover component (#7005) Co-authored-by: katiegoines --- .../__tests__/GetStartedPopover.test.tsx | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/components/GetStartedPopover/__tests__/GetStartedPopover.test.tsx diff --git a/src/components/GetStartedPopover/__tests__/GetStartedPopover.test.tsx b/src/components/GetStartedPopover/__tests__/GetStartedPopover.test.tsx new file mode 100644 index 00000000000..925907e989b --- /dev/null +++ b/src/components/GetStartedPopover/__tests__/GetStartedPopover.test.tsx @@ -0,0 +1,132 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { GetStartedPopover } from '../index'; +import userEvent from '@testing-library/user-event'; + +const routerMock = { + __esModule: true, + useRouter: () => { + return { + query: { platform: 'react' } + }; + } +}; + +jest.mock('next/router', () => routerMock); + +describe('GetStartedPopover', () => { + const component = ; + + it('should render the GetStartedPopover component', async () => { + render(component); + + const gettingStartedBtn = await screen.findByRole('link', { + name: 'Get started' + }); + + expect(gettingStartedBtn).toBeInTheDocument(); + }); + + it('should link to the selected platform on click of "Getting started"', async () => { + render(component); + + const popoverNode = await screen.findByRole('link', { + name: 'Get started' + }); + expect(popoverNode.href).toContain( + '/react/start/getting-started/introduction' + ); + }); + + it('should show platform options on click of split button', async () => { + render(component); + + const button = await screen.findByRole('button', { + name: 'Toggle getting started guides navigation' + }); + const dropdown = await screen.findByRole('navigation', { + name: 'Getting started guides for other platforms' + }); + + expect(dropdown.classList).not.toContain('popover--expanded'); + userEvent.click(button); + expect(dropdown.classList).toContain('popover--expanded'); + }); + + it('should show each platform option with link to corresponding Getting Started page', async () => { + render(component); + + const swiftOption = await screen.findByRole('link', { name: 'Swift' }); + const angularOption = await screen.findByRole('link', { name: 'Angular' }); + const nextjsOption = await screen.findByRole('link', { name: 'Next.js' }); + expect(swiftOption.href).toContain( + '/swift/start/getting-started/introduction' + ); + expect(angularOption.href).toContain( + '/angular/start/getting-started/introduction' + ); + expect(nextjsOption.href).toContain( + '/nextjs/start/getting-started/introduction' + ); + }); + + it('should minimize dropdown on click outside dropdown', async () => { + render(component); + const button = await screen.findByRole('button', { + name: 'Toggle getting started guides navigation' + }); + const dropdown = await screen.findByRole('navigation', { + name: 'Getting started guides for other platforms' + }); + + userEvent.click(button); + expect(dropdown.classList).toContain('popover--expanded'); + userEvent.click(document.body); + expect(dropdown.classList).not.toContain('popover--expanded'); + }); + + it('should minimize dropdown on tab after last platform option', async () => { + render(component); + const button = await screen.findByRole('button', { + name: 'Toggle getting started guides navigation' + }); + const dropdown = await screen.findByRole('navigation', { + name: 'Getting started guides for other platforms' + }); + const platformOptions = + document.getElementsByClassName('popover-list__link'); + + userEvent.click(button); + expect(dropdown.classList).toContain('popover--expanded'); + userEvent.tab(); + expect(platformOptions[0].textContent).toBe('React'); + expect(platformOptions[0]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[1].textContent).toBe('JavaScript'); + expect(platformOptions[1]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[2].textContent).toBe('Flutter'); + expect(platformOptions[2]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[3].textContent).toBe('Swift'); + expect(platformOptions[3]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[4].textContent).toBe('Android'); + expect(platformOptions[4]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[5].textContent).toBe('React Native'); + expect(platformOptions[5]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[6].textContent).toBe('Angular'); + expect(platformOptions[6]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[7].textContent).toBe('Next.js'); + expect(platformOptions[7]).toHaveFocus(); + userEvent.tab(); + expect(platformOptions[8].textContent).toBe('Vue'); + expect(platformOptions[8]).toHaveFocus(); + userEvent.tab(); + expect(dropdown.classList).not.toContain('popover--expanded'); + expect(dropdown).not.toHaveFocus(); + }); +}); From 2bed645aedf3e949901ed92eaacbbb73b88955fc Mon Sep 17 00:00:00 2001 From: Dan Kiuna Date: Wed, 6 Mar 2024 11:19:35 -0600 Subject: [PATCH 40/91] fix: [Storage] Configure Access (#6948) Co-authored-by: Tim Nguyen <54393192+timngyn@users.noreply.github.com> --- .../build-a-backend/storage/configure-access/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/[platform]/build-a-backend/storage/configure-access/index.mdx b/src/pages/[platform]/build-a-backend/storage/configure-access/index.mdx index 7d37d5cfdf2..ebeeb9a655e 100644 --- a/src/pages/[platform]/build-a-backend/storage/configure-access/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/configure-access/index.mdx @@ -62,7 +62,7 @@ If you had previously enabled user sign-in by running `amplify add auth` in your - Protected: Readable by all users, but writable only by the creating user. Files are stored under `protected/{user_identity_id}/` where the `user_identity_id` corresponds to the unique Amazon Cognito Identity ID for that user. - Private: Only accessible for the individual user. Files are stored under `private/{user_identity_id}/` where the `user_identity_id` corresponds to the unique Amazon Cognito Identity ID for that user. -When using Auth and Storage modules together, you do not need to construct the `/{user_identity_id}/` manually as the library will use the configured Cognito Identity ID for your user/device along with the configured access level for an action. This includes UnAuthenticated access where you will first call `Auth.currentCredentials()` before a Storage action. See [Authentication](/[platform]/build-a-backend/auth/under-the-hood/) for more information. +When using Auth and Storage modules together, you do not need to construct the `/{user_identity_id}/` manually as the library will use the configured Cognito Identity ID for your user/device along with the configured access level for an action. This includes UnAuthenticated access where you will first call `fetchAuthSession` before a Storage action. See [Authentication](/[platform]/build-a-backend/auth/under-the-hood/) for more information. The access level can be configured on the Storage object globally. Alternatively, the access levels can be set in individual function calls. From 684333aa8fc3436624edccbe3009b69ab9f9e8d2 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:28:31 -0800 Subject: [PATCH 41/91] fix(Swift): update data use policy info's tracking to false (#6938) * fix(Swift): update data use policy info's tracking to false * Update data-information.mdx --- src/fragments/lib/info/ios/data-information.mdx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/fragments/lib/info/ios/data-information.mdx b/src/fragments/lib/info/ios/data-information.mdx index ffe9ff11538..58f527c505d 100644 --- a/src/fragments/lib/info/ios/data-information.mdx +++ b/src/fragments/lib/info/ios/data-information.mdx @@ -1,4 +1,8 @@ -Apple requires app developers to provide the data usage policy of the app when they submit their app to the App Store. Below are the different categories identified by Apple and the corresponding data type used by the Amplify library. +Apple requires app developers to provide the data usage policy of the app when they submit their app to the App Store. See Apple's [User privacy and data use](https://developer.apple.com/app-store/user-privacy-and-data-use/) for more details. Amplify Library is used to interact with AWS resources under the developer’s ownership and management. The library cannot predict the usage of its APIs and it is up to the developer to provide the privacy manifest that accurately reflects the data collected by the app. Below are the different categories identified by Apple and the corresponding data type used by the Amplify Library. + +By utilizing the library, Amplify gathers API usage metrics from the AWS services accessed. This process involves adding a user agent to the request made to your AWS service. The user-agent header is included with information about the Amplify Library version, operating system name, and version. AWS collects this data to generate metrics related to our library usage. This information is not linked to the user’s identity and not used for tracking purposes as described in Apple's privacy and data use guidelines. + +Should you have any specific concerns or require additional information for the enhancement of your privacy manifest, please don't hesitate to reach out. ## Contact info @@ -27,21 +31,21 @@ Apple requires app developers to provide the data usage policy of the app when t | ------------------------------ | ------------------ | ------------------- | :------------------: | :--------: | :---------------------: | | **User ID** | | | | | | | | Auth | App Functionality | ✅ | ❌ | ❌ | -| | Analytics | Analytics | ✅ | ✅ | ❌ | +| | Analytics | Analytics | ✅ | ❌ | ❌ | | **Device ID** | | | | | | | | Auth | App Functionality | ✅ | ❌ | ❌ | -| | Analytics | Analytics | ✅ | ✅ | ❌ | +| | Analytics | Analytics | ✅ | ❌ | ❌ | ## Other Data | Data Type | Amplify Category | Purpose | Linked To Identity | Tracking | Provided by developer | | ------------------------------ | ------------------ | ------------------- | :------------------: | :--------: | :---------------------: | | **OS Version** | | | | | | -| | All categories | Analytics | ❌ | ✅ | ❌ | +| | All categories | Analytics | ❌ | ❌ | ❌ | | **OS Name** | | | | | | -| | All categories | Analytics | ❌ | ✅ | ❌ | +| | All categories | Analytics | ❌ | ❌ | ❌ | | **Locale Info** | | | | | | -| | All categories | Analytics | ❌ | ✅ | ❌ | +| | All categories | Analytics | ❌ | ❌ | ❌ | | **App Version** | | | | | | | | Auth | App Functionality | ✅ | ❌ | ❌ | | **Min OS target of the app** | | | | | | From 6d37f783778857d8e2f8a29ff109eca5fc6c9a16 Mon Sep 17 00:00:00 2001 From: erinleigh90 <106691284+erinleigh90@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:25:06 -0700 Subject: [PATCH 42/91] chore: add callouts and troubleshooting guide for configuration (#6967) --- src/directory/directory.mjs | 3 + .../getting-started/30_initAnalytics.mdx | 6 + src/fragments/lib/geo/js/getting-started.mdx | 6 + .../integrate-your-application.mdx | 6 + .../lib/interactions/js/getting-started.mdx | 12 ++ .../lib/predictions/js/getting-started.mdx | 6 + .../restapi/js/getting-started/30_initapi.mdx | 6 + .../auth/set-up-auth/index.mdx | 18 ++ .../graphqlapi/connect-to-api/index.mdx | 6 + .../server-side-rendering/index.mdx | 2 + .../server-side-rendering/nextjs/index.mdx | 8 +- .../server-side-rendering/nuxt/index.mdx | 12 ++ .../storage/set-up-storage/index.mdx | 6 + .../library-not-configured/index.mdx | 195 ++++++++++++++++++ .../start/getting-started/setup/index.mdx | 6 + 15 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 src/pages/[platform]/build-a-backend/troubleshooting/library-not-configured/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 2be06067343..29c53ead8fc 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -786,6 +786,9 @@ export const directory = { }, { path: 'src/pages/[platform]/build-a-backend/troubleshooting/migrate-from-javascript-v5-to-v6/index.mdx' + }, + { + path: 'src/pages/[platform]/build-a-backend/troubleshooting/library-not-configured/index.mdx' } ] } diff --git a/src/fragments/lib/analytics/native_common/getting-started/30_initAnalytics.mdx b/src/fragments/lib/analytics/native_common/getting-started/30_initAnalytics.mdx index d4f1ba0de64..c151bf9afc0 100644 --- a/src/fragments/lib/analytics/native_common/getting-started/30_initAnalytics.mdx +++ b/src/fragments/lib/analytics/native_common/getting-started/30_initAnalytics.mdx @@ -13,4 +13,10 @@ import amplifyconfig from './amplifyconfiguration.json'; Amplify.configure(amplifyconfig); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + User session data is automatically collected unless you disabled analytics. To see the results visit the [Amazon Pinpoint console](https://console.aws.amazon.com/pinpoint/home/). diff --git a/src/fragments/lib/geo/js/getting-started.mdx b/src/fragments/lib/geo/js/getting-started.mdx index b07095d6bc1..36bde527453 100644 --- a/src/fragments/lib/geo/js/getting-started.mdx +++ b/src/fragments/lib/geo/js/getting-started.mdx @@ -72,3 +72,9 @@ import { Amplify } from 'aws-amplify'; import amplifyconfig from './amplifyconfiguration.json'; Amplify.configure(amplifyconfig); ``` + + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + diff --git a/src/fragments/lib/in-app-messaging/integrate-your-application/integrate-your-application.mdx b/src/fragments/lib/in-app-messaging/integrate-your-application/integrate-your-application.mdx index e53eea79488..5b407c32cf5 100644 --- a/src/fragments/lib/in-app-messaging/integrate-your-application/integrate-your-application.mdx +++ b/src/fragments/lib/in-app-messaging/integrate-your-application/integrate-your-application.mdx @@ -17,6 +17,12 @@ Amplify.configure(amplifyconfig); initializeInAppMessaging(); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + import reactnative0 from '/src/fragments/lib/in-app-messaging/integrate-your-application/react-native/install-dependencies.mdx'; import js0 from '/src/fragments/lib/in-app-messaging/integrate-your-application/js/install-ui-dependencies.mdx'; diff --git a/src/fragments/lib/interactions/js/getting-started.mdx b/src/fragments/lib/interactions/js/getting-started.mdx index 11da4334d5d..10179daafea 100644 --- a/src/fragments/lib/interactions/js/getting-started.mdx +++ b/src/fragments/lib/interactions/js/getting-started.mdx @@ -53,6 +53,12 @@ Amplify.configure({ }); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + ## Setup AWS LexV1 bot ### Create new LexV1 chatbot with Amplify CLI @@ -135,6 +141,12 @@ Amplify.configure({ }); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + ## Configure frontend diff --git a/src/fragments/lib/predictions/js/getting-started.mdx b/src/fragments/lib/predictions/js/getting-started.mdx index e0f69dd6e75..d5650ab749b 100644 --- a/src/fragments/lib/predictions/js/getting-started.mdx +++ b/src/fragments/lib/predictions/js/getting-started.mdx @@ -72,6 +72,12 @@ import amplifyconfig from './amplifyconfiguration.json'; Amplify.configure(amplifyconfig); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + ## Import existing backend The manual setup enables you to use your existing Amazon AI and ML resources in your app. diff --git a/src/fragments/lib/restapi/js/getting-started/30_initapi.mdx b/src/fragments/lib/restapi/js/getting-started/30_initapi.mdx index b2a9b7bc521..a76615479b2 100644 --- a/src/fragments/lib/restapi/js/getting-started/30_initapi.mdx +++ b/src/fragments/lib/restapi/js/getting-started/30_initapi.mdx @@ -8,3 +8,9 @@ import amplifyconfig from './amplifyconfiguration.json'; Amplify.configure(amplifyconfig); ``` + + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + diff --git a/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx b/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx index d914369cad9..4364e72472e 100644 --- a/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx @@ -161,6 +161,12 @@ import config from './amplifyconfiguration.json'; Amplify.configure(config); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + +
@@ -207,6 +213,12 @@ import config from './amplifyconfiguration.json'; Amplify.configure(config); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + @@ -237,6 +249,12 @@ Amplify Studio allows you to create auth resources, set up authorization rules, Existing Authentication resources from AWS (such as Amazon Cognito User Pools or Identity Pools) can be used with the Amplify Libraries by calling the `Amplify.configure()` method. + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + In your app's entry point (specifically **App.js**, **index.js**, **\_app.js**, or **main.js**), import and load the configuration file: ```javascript diff --git a/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx b/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx index 0f57d393859..3a5565f0818 100644 --- a/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx +++ b/src/pages/[platform]/build-a-backend/graphqlapi/connect-to-api/index.mdx @@ -74,6 +74,12 @@ import config from './amplifyconfiguration.json'; Amplify.configure(config); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx index 28c47d8c449..71e22944d57 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/index.mdx @@ -52,6 +52,8 @@ You will need to use the `runWithAmplifyServerContext` function (exported from ` **NOTE:** You will need to call `Amplify.configure()` on the client side of your app by setting `ssr` to `true`. E.g., `Amplify.configure(config, { ssr: true })` so that the requests sent to your server will have Amplify auth tokens in the cookie header. +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + ### Using the `runWithAmplifyServerContext` Function diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx index bd3104dc21f..6916c449af9 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nextjs/index.mdx @@ -78,7 +78,7 @@ You can use the exported `runWithAmplifyServerContext` function to call Amplify ## Configure Amplify library for client-side usage -When you use the Amplify library on the client-side of your Next.js app, you will need to configure Amplify by calling the Amplify.configure as you would to use Amplify in a single-page application. +When you use the Amplify library on the client-side of your Next.js app, you will need to configure Amplify by calling `Amplify.configure` as you would to use Amplify in a single-page application. @@ -105,6 +105,12 @@ export default function RootLayoutThatConfiguresAmplifyOnTheClient({ } ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + To avoid repetitive calls to `Amplify.configure`, you can call it once in a top-level client-side rendered layout component. diff --git a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx index 5f582d4cc76..8f02a975cc6 100644 --- a/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx +++ b/src/pages/[platform]/build-a-backend/server-side-rendering/nuxt/index.mdx @@ -123,6 +123,12 @@ export default defineNuxtPlugin({ }); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + +
### Implement `01.amplify-apis.server.ts` @@ -475,6 +481,12 @@ export default defineNuxtPlugin({ }); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + +
## Set Up Amplify for API Route Use Cases diff --git a/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx b/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx index 3aa62eb6df6..97732034679 100644 --- a/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx @@ -131,6 +131,12 @@ import amplifyconfig from './amplifyconfiguration.json'; Amplify.configure(amplifyconfig); ``` + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + ## Mocking and Local Testing with Amplify CLI Amplify CLI supports running a local mock server for testing your application with Amazon S3. Please see the [CLI toolchain documentation](/[platform]/tools/cli/usage/mock/) for more details. diff --git a/src/pages/[platform]/build-a-backend/troubleshooting/library-not-configured/index.mdx b/src/pages/[platform]/build-a-backend/troubleshooting/library-not-configured/index.mdx new file mode 100644 index 00000000000..983dd610892 --- /dev/null +++ b/src/pages/[platform]/build-a-backend/troubleshooting/library-not-configured/index.mdx @@ -0,0 +1,195 @@ +import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; + +export const meta = { + title: 'Troubleshoot configuration errors', + description: 'Addressing missing configuration or NoCredentials error messages', + platforms: [ + 'angular', + 'javascript', + 'nextjs', + 'react', + 'react-native', + 'vue' + ], + canonicalObjects: [ + { + platforms: [ + 'angular', + 'react', + 'react-native', + 'vue', + 'nextjs', + 'javascript' + ], + canonicalPath: '/javascript/build-a-backend/troubleshooting/library-not-configured/' + } + ] +}; + +export const getStaticPaths = async () => { + return getCustomStaticPath(meta.platforms); +}; + +export function getStaticProps(context) { + return { + props: { + platform: context.params.platform, + meta + } + }; +} + +If you are running into a missing configuration or `NoCredentials` error message and have called `Amplify.configure` in your project, your Amplify API is most likely being called before `Amplify.configure`. This can happen in a few different ways. Below are three possibilities you can check to troubleshoot this issue. + +## Check 1: Validate that `Amplify.configure` is called in the root of your project + +Make sure you are calling `Amplify.configure` in the root file of your project. The root file of your app may be different depending on your frontend framework. The current default for some common frameworks are listed below (if you are not using TypeScript the `ts` and `tsx` extensions would be `js` and `jsx`): + +* Vue.js: **src/main.ts** +* React: **src/main.tsx** +* Angular: **src/main.ts** +* Next.js Page Router: **pages/_app.tsx** or **src/pages/_app.tsx** +* Nuxt: **app.vue** (Or in a plugins file, as recommended [here](https://docs.amplify.aws/react/build-a-backend/server-side-rendering/nuxt/).) + + + +If you are using the Next.js App Router, you can follow the suggestions in our [Next.js documentation](/[platform]/build-a-backend/server-side-rendering/nextjs/#configure-amplify-library-for-client-side-usage) for root-level configuration. Keep in mind that if you are calling any APIs at the module-level (i.e. at the top of your file) in any of the Child components, you may still run into this issue. Continue on the [Check 2](http://localhost:3000/react/build-a-backend/troubleshooting/library-not-configured/#check-2-move-module-level-amplify-api-invocations) if this is the case. + + + +## Check 2: Move module-level Amplify API invocations + +When Amplify APIs are used outside of your application lifecycle, there is a risk that a JavaScript bundler may place that API call before `Amplify.configure`. Module-level function calls (calls at the top-level of a file), are generally evaluated in the order that they are imported. + +Below is an example of code that will likely result in a missing configuration or `NoCredentials` error message: + +```tsx title="index.ts" +import { Amplify } from 'aws-amplify'; +import ComponentX from 'module-fetch-auth'; + +// fetchAuthSession() in ComponentX executed on import + +Amplify.configure(); + +export default function App() { + return ( +
+ +
+ ); +} +``` + +```tsx title="module-fetch-auth.tsx" +import { fetchAuthSession } from 'aws-amplify/auth'; + +fetchAuthSession(); // Will throw "AuthUserPoolException: Auth UserPool not configured." + +export default function ComponentX() { + return ( +
+ ... +
+ ); +} +``` + +This error can also happen when using Next.js Layouts and calling Amplify APIs in child components at the module-level (at the top of your file/module). See below for an example of this issue: + +```tsx title="layout.tsx" +import ConfigureAmplifyClientSide from '@/ConfigureAmplifyClientSide'; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + <> + + {children} + + + + ); +} +``` + +```tsx title="ConfigureAmplifyClientSide.tsx" +import { Amplify } from "aws-amplify"; + +Amplify.configure(config, { ssr: true }); + +export default function ConfigureAmplifyClientSide() { + return null; +} +``` + +```tsx title="page.tsx" +import { fetchAuthSession } from "aws-amplify/auth"; + +// The layout calls configure, but fetchAuthSession ends up executing first +// Will throw "AuthUserPoolException: Auth UserPool not configured." +fetchAuthSession().then((session) => { + console.log(session); +}); + +export default function HomePage() { + return ( +
+ ... +
+ ); +} +``` + +To fix this, we suggest moving all Amplify API calls to within the application lifecycle. For instance, if you are using **React**, you can use the `useEffect` hook for functions that should run before the app is loaded: + +```tsx title="index.ts" +import { Amplify } from 'aws-amplify'; +import ComponentX from 'module-fetch-auth'; + +Amplify.configure(); + +export default function App() { + return ( +
+ +
+ ); +} +``` + +```tsx title="module-fetch-auth.tsx" +import { type AuthSession, fetchAuthSession } from 'aws-amplify/auth'; +import { useEffect, useState } from 'react'; + +export default function ComponentX() { + const [session, setSession] = useState(); + + const getSession = async () => { + try { + const currentSession = await fetchAuthSession(); + setSession(currentSession); + } catch (error: unknown) { + console.log(error); + } + }; + + useEffect(() => { + getSession(); + }, []); + + return ( +
+ ... +
+ ); +} +``` + +## Check 3: Configure Amplify on each page of a multi-page app + +If you are working in a multi-page app, you need to call `Amplify.configure()` for each page/route of your application. We recommend calling `Amplify.configure` in a common source file and importing it into each page. diff --git a/src/pages/[platform]/start/getting-started/setup/index.mdx b/src/pages/[platform]/start/getting-started/setup/index.mdx index df5b2c82ee5..e208f6ba5e4 100644 --- a/src/pages/[platform]/start/getting-started/setup/index.mdx +++ b/src/pages/[platform]/start/getting-started/setup/index.mdx @@ -731,6 +731,12 @@ In order to import `amplifyconfiguration.json` files in Typescript, you may need + + +Make sure you call `Amplify.configure` as early as possible in your application’s life-cycle. A missing configuration or `NoCredentials` error is thrown if `Amplify.configure` has not been called before other Amplify JavaScript APIs. Review the [Library Not Configured Troubleshooting guide](/[platform]/build-a-backend/troubleshooting/library-not-configured/) for possible causes of this issue. + + + And that's all it takes to configure Amplify. As you add or remove categories and make updates to your backend configuration using the Amplify CLI, the configuration in **amplifyconfiguration.json** will update automatically. Now that your app is set up and Amplify is initialized, you can add an API in the next step. From 25e641b981a3443b0457489023c8b21a9a96d3f7 Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Wed, 6 Mar 2024 12:41:43 -0800 Subject: [PATCH 43/91] fix: resolve in-content broken links (#7011) * re-platforming * remove reference to mocking and testing for android & swift * remove reference to mocking and testing for android & swift * filtered out reference to optimistic ui for react native --------- Co-authored-by: katiegoines --- .../lib-v1/graphqlapi/js/upgrade-guide.mdx | 2 ++ src/fragments/sdk/api/android/graphql.mdx | 4 ---- src/fragments/sdk/api/ios/graphql.mdx | 4 ---- .../sdk/storage/android/getting-started.mdx | 4 ---- .../sdk/storage/ios/getting-started.mdx | 4 ---- .../formbuilder/call-to-action/index.mdx | 6 ++++-- .../build-ui/formbuilder/customize/index.mdx | 6 ++++-- .../formbuilder/data-binding/index.mdx | 6 ++++-- .../[platform]/build-ui/formbuilder/index.mdx | 6 ++++-- .../build-ui/formbuilder/lifecycle/index.mdx | 6 ++++-- .../build-ui/formbuilder/overrides/index.mdx | 6 ++++-- .../formbuilder/special-inputs/index.mdx | 6 ++++-- .../build-ui/formbuilder/validations/index.mdx | 6 ++++-- .../build-ui/uibuilder/bestpractices/index.mdx | 6 ++++-- .../build-ui/uibuilder/collections/index.mdx | 6 ++++-- .../build-ui/uibuilder/databinding/index.mdx | 6 ++++-- .../build-ui/uibuilder/eventhandling/index.mdx | 6 ++++-- .../[platform]/build-ui/uibuilder/index.mdx | 6 ++++-- .../build-ui/uibuilder/override/index.mdx | 6 ++++-- .../build-ui/uibuilder/responsive/index.mdx | 6 ++++-- .../build-ui/uibuilder/slots/index.mdx | 6 ++++-- .../build-ui/uibuilder/theming/index.mdx | 6 ++++-- .../tools/console/adminui/start/index.mdx | 2 ++ .../tools/console/data/data-model/index.mdx | 2 ++ .../tools/console/tutorial/bindui/index.mdx | 18 +++--------------- .../tools/console/tutorial/buildui/index.mdx | 16 ++-------------- .../tools/console/tutorial/code/index.mdx | 16 ++-------------- .../console/tutorial/collections/index.mdx | 18 +++--------------- .../tools/console/tutorial/data/index.mdx | 14 +------------- .../tools/console/tutorial/index.mdx | 14 +------------- 30 files changed, 86 insertions(+), 134 deletions(-) diff --git a/src/fragments/lib-v1/graphqlapi/js/upgrade-guide.mdx b/src/fragments/lib-v1/graphqlapi/js/upgrade-guide.mdx index e5f500b7eaa..bc3dfed62ca 100644 --- a/src/fragments/lib-v1/graphqlapi/js/upgrade-guide.mdx +++ b/src/fragments/lib-v1/graphqlapi/js/upgrade-guide.mdx @@ -55,9 +55,11 @@ API.graphql({ }); ``` + ### Optimistic UI and cached data revalidation If you've used the AWS AppSync SDK's caching capabilities for optimistic UI, then we recommend you to follow our [Optimistic UI](/[platform]/prev/build-a-backend/graphqlapi/optimistic-ui/) guide. In this guide, you'll learn how to use AWS Amplify's API category in conjunction with [TanStack Query](https://tanstack.com/query/latest) to achieve optimistic UI and cached data invalidation use cases. + ### Complex objects support diff --git a/src/fragments/sdk/api/android/graphql.mdx b/src/fragments/sdk/api/android/graphql.mdx index 2482c0a21e4..33e1390192d 100644 --- a/src/fragments/sdk/api/android/graphql.mdx +++ b/src/fragments/sdk/api/android/graphql.mdx @@ -318,10 +318,6 @@ try { } ``` -### Mocking and Local Testing - -Amplify supports running a local mock server for testing your application with AWS AppSync, including debugging of resolvers, before pushing to the cloud. Please see the [CLI Toolchain documentation](/[platform]/tools/cli/usage/mock/) for more details. - ### Client Architecture The AppSync client supports offline scenarios with a programming model that provides a "write through cache". This allows you to both render data in the UI when offline as well as add/update through an "optimistic response". The below diagram shows how the AppSync client interfaces with the network GraphQL calls, it's offline mutation queue, the Apollo cache, and your application code. diff --git a/src/fragments/sdk/api/ios/graphql.mdx b/src/fragments/sdk/api/ios/graphql.mdx index e38e5c90d7e..5de1882f3c2 100644 --- a/src/fragments/sdk/api/ios/graphql.mdx +++ b/src/fragments/sdk/api/ios/graphql.mdx @@ -252,10 +252,6 @@ do { Like mutations, subscriptions can also take input types, in which case they will be subscribing to particular events based on the input. To learn more about subscription arguments, see [AWS AppSync Subscription Arguments](https://docs.aws.amazon.com/appsync/latest/devguide/real-time-data.html#using-subscription-arguments). -### Mocking and Local Testing - -Amplify supports running a local mock server for testing your application with AWS AppSync, including debugging of resolvers, before pushing to the cloud. Please see the [CLI Toolchain documentation](/[platform]/tools/cli/usage/mock/) for more details. - ### Client Architecture The AppSync client supports offline scenarios with a programming model that provides a "write through cache". This allows you to both render data in the UI when offline as well as add/update through an "optimistic response". The below diagram shows how the AppSync client interfaces with the network GraphQL calls, its offline mutation queue, the Apollo cache, and your application code. diff --git a/src/fragments/sdk/storage/android/getting-started.mdx b/src/fragments/sdk/storage/android/getting-started.mdx index 749ec87155e..b0161206ed5 100644 --- a/src/fragments/sdk/storage/android/getting-started.mdx +++ b/src/fragments/sdk/storage/android/getting-started.mdx @@ -94,10 +94,6 @@ Use the following steps to connect add file storage backend services to your app ``` -## Mocking and Local Testing - -Amplify supports running a local mock server for testing your application with S3. Please see the [CLI Toolchain documentation](/[platform]/tools/cli/usage/mock/) for more details. - ## Note on Transfer Utility and Amazon Cognito Transfer Utility generates Amazon S3 pre-signed URLs and uses them to enable background transferring. Using Amazon Cognito Identity, you receive AWS temporary credentials that are valid for up to 60 minutes. It is not possible to generate S3 pre-signed URLs that last longer than 60 minutes. Due to this limitation, the Transfer Utility enforces 50-minute transfer timeouts (10-minute buffer for reducing AWS temporary credential regenerations). After 50 minutes, you receive a transfer failure. diff --git a/src/fragments/sdk/storage/ios/getting-started.mdx b/src/fragments/sdk/storage/ios/getting-started.mdx index 31961d92913..66eb2f0a844 100644 --- a/src/fragments/sdk/storage/ios/getting-started.mdx +++ b/src/fragments/sdk/storage/ios/getting-started.mdx @@ -86,7 +86,3 @@ Run `pod install --repo-update` before you continue. ```swift import AWSS3 ``` - -### Mocking and Local Testing - -Amplify supports running a local mock server for testing your application with S3. Please see the [CLI Toolchain documentation](/[platform]/tools/cli/usage/mock/) for more details. diff --git a/src/pages/[platform]/build-ui/formbuilder/call-to-action/index.mdx b/src/pages/[platform]/build-ui/formbuilder/call-to-action/index.mdx index 2f328a8d81a..600077c426d 100644 --- a/src/pages/[platform]/build-ui/formbuilder/call-to-action/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/call-to-action/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Amplify Studio generated forms come with three action buttons: **Submit**, **Cancel**, and **Clear** or **Reset**, depending on whether the form creates or updates a record.', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/call-to-action/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/customize/index.mdx b/src/pages/[platform]/build-ui/formbuilder/customize/index.mdx index 256cf645a8f..4fdf76e8f9c 100644 --- a/src/pages/[platform]/build-ui/formbuilder/customize/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/customize/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Use the Form Builder in Amplify Studio to customize React form components. You can add new form inputs, bind them to a field, customize labels, and add validation rules.', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/customize/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/data-binding/index.mdx b/src/pages/[platform]/build-ui/formbuilder/data-binding/index.mdx index 08d39c7db0b..aa93601336a 100644 --- a/src/pages/[platform]/build-ui/formbuilder/data-binding/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/data-binding/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Cloud connected forms can be bound to data models with relationships, allowing multiple data models to be updated upon submission.', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/data-binding/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/index.mdx b/src/pages/[platform]/build-ui/formbuilder/index.mdx index 83a8933dd72..420a1349dba 100644 --- a/src/pages/[platform]/build-ui/formbuilder/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: "Amplify Studio's Form Builder automatically generates cloud-connected forms as React code, either from your data model, any JSON object, or from scratch. You can configure validation logic, adjust theming, and customize presentation all within the console.", platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/lifecycle/index.mdx b/src/pages/[platform]/build-ui/formbuilder/lifecycle/index.mdx index d4f9c3eb543..36ced1c7d37 100644 --- a/src/pages/[platform]/build-ui/formbuilder/lifecycle/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/lifecycle/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: "Hook into the form's lifecycle events to customize user input before submission, run validations, handle errors, or self-manage user input events.", platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'react', - 'javascript' + 'javascript', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/lifecycle/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/overrides/index.mdx b/src/pages/[platform]/build-ui/formbuilder/overrides/index.mdx index d93deddd33e..6b7813c0ff7 100644 --- a/src/pages/[platform]/build-ui/formbuilder/overrides/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/overrides/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: "Use the \'overrides\' property to override any form input's properties. Use this as an escape hatch in case there's a property that you can't customize within Studio.", platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'react', - 'javascript' + 'javascript', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/overrides/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/special-inputs/index.mdx b/src/pages/[platform]/build-ui/formbuilder/special-inputs/index.mdx index 2b35ea83827..5cf952ce2c6 100644 --- a/src/pages/[platform]/build-ui/formbuilder/special-inputs/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/special-inputs/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Special input fields in Amplify Studio form builder allow the user to interact with unique Amplify features', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'react', - 'javascript' + 'javascript', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/special-inputs/' } diff --git a/src/pages/[platform]/build-ui/formbuilder/validations/index.mdx b/src/pages/[platform]/build-ui/formbuilder/validations/index.mdx index 75e894ef1cf..74a0fbd0f48 100644 --- a/src/pages/[platform]/build-ui/formbuilder/validations/index.mdx +++ b/src/pages/[platform]/build-ui/formbuilder/validations/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: "Sanitize user input by adding validation rules to your form. By default, Amplify Studio infers a range of validation rules based on the data model. For example, given a data model with an 'AWSEmail' field, the generated form input will automatically run an email validation rule.", platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/formbuilder/validations/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/bestpractices/index.mdx b/src/pages/[platform]/build-ui/uibuilder/bestpractices/index.mdx index 123b7b82f75..a95bb7dbfcb 100644 --- a/src/pages/[platform]/build-ui/uibuilder/bestpractices/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/bestpractices/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: "Constraints of Amplify Studio's Figma to React capabilities", platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'react', - 'javascript' + 'javascript', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/bestpractices/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/collections/index.mdx b/src/pages/[platform]/build-ui/uibuilder/collections/index.mdx index 349ea30a3fc..9563ee32951 100644 --- a/src/pages/[platform]/build-ui/uibuilder/collections/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/collections/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Collections', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/collections/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/databinding/index.mdx b/src/pages/[platform]/build-ui/uibuilder/databinding/index.mdx index 39745aff674..530bb90dc18 100644 --- a/src/pages/[platform]/build-ui/uibuilder/databinding/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/databinding/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Figma to React code with Amplify Studio', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/databinding/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/eventhandling/index.mdx b/src/pages/[platform]/build-ui/uibuilder/eventhandling/index.mdx index edc7ce15bc2..953506c8fbe 100644 --- a/src/pages/[platform]/build-ui/uibuilder/eventhandling/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/eventhandling/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Figma to React code with Amplify Studio', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/eventhandling/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/index.mdx b/src/pages/[platform]/build-ui/uibuilder/index.mdx index 290a87f7b2f..a97c4788b1b 100644 --- a/src/pages/[platform]/build-ui/uibuilder/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Generate clean React code from Figma design files with Amplify Studio.', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'react', - 'javascript' + 'javascript', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/override/index.mdx b/src/pages/[platform]/build-ui/uibuilder/override/index.mdx index a708bd821e3..9db1b0f671c 100644 --- a/src/pages/[platform]/build-ui/uibuilder/override/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/override/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Figma to React code with Amplify Studio', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/override/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/responsive/index.mdx b/src/pages/[platform]/build-ui/uibuilder/responsive/index.mdx index b878ffad7b4..36bd0cddbdc 100644 --- a/src/pages/[platform]/build-ui/uibuilder/responsive/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/responsive/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Learn how to configure Figma to Code components in order for them to scale according to breakpoints', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'react', - 'javascript' + 'javascript', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/responsive/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/slots/index.mdx b/src/pages/[platform]/build-ui/uibuilder/slots/index.mdx index c580a29a199..23d885e7cc4 100644 --- a/src/pages/[platform]/build-ui/uibuilder/slots/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/slots/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Add component slots to Amplify-generated Figma to code components. Use this to support nested components or collections in React code.', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/slots/' } diff --git a/src/pages/[platform]/build-ui/uibuilder/theming/index.mdx b/src/pages/[platform]/build-ui/uibuilder/theming/index.mdx index c3d21a9e934..9ae928d8b7b 100644 --- a/src/pages/[platform]/build-ui/uibuilder/theming/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/theming/index.mdx @@ -5,13 +5,15 @@ export const meta = { description: 'Configure your Amplify-generated UI components to match your brand using the Amplify Theme Editor Figma plugin', platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalObjects: [ { platforms: [ 'javascript', - 'react' + 'react', + 'nextjs' ], canonicalPath: '/javascript/build-ui/uibuilder/theming/' } diff --git a/src/pages/[platform]/tools/console/adminui/start/index.mdx b/src/pages/[platform]/tools/console/adminui/start/index.mdx index 6055606f5c2..f95859f3d5b 100644 --- a/src/pages/[platform]/tools/console/adminui/start/index.mdx +++ b/src/pages/[platform]/tools/console/adminui/start/index.mdx @@ -146,5 +146,7 @@ If you have [created a new Amplify project through the CLI](/[platform]/tools/cl You are ready to start creating and managing your application's backend in Amplify Studio. Recommended next steps: - [Build a data model](/[platform]/tools/console/data/data-model/) + - [Create UI components in Figma](/[platform]/build-ui/uibuilder/) + - [Invite your team members to collaborate](/[platform]/tools/console/adminui/access-management/) diff --git a/src/pages/[platform]/tools/console/data/data-model/index.mdx b/src/pages/[platform]/tools/console/data/data-model/index.mdx index ece3a6b7957..740e5649647 100644 --- a/src/pages/[platform]/tools/console/data/data-model/index.mdx +++ b/src/pages/[platform]/tools/console/data/data-model/index.mdx @@ -111,8 +111,10 @@ To configure your API using the Amplify CLI, run `amplify configure api` in your Applications with Conflict Resolution disabled have some minor limitations: - Sorting by column in [Data Manager](/[platform]/tools/console/data/content-management/) is not supported + - [Figma-to-Code Collections](/[platform]/build-ui/uibuilder/collections/) do not support Pagination, and have limited support for Search - [Forms with relationships](/[platform]/build-ui/formbuilder/special-inputs/) have some limitations when searching Autocomplete fields + To get access to these features, turn on DataStore and select a Conflict Resolution strategy. diff --git a/src/pages/[platform]/tools/console/tutorial/bindui/index.mdx b/src/pages/[platform]/tools/console/tutorial/bindui/index.mdx index a277bf0c6a9..e9c0fb92ea1 100644 --- a/src/pages/[platform]/tools/console/tutorial/bindui/index.mdx +++ b/src/pages/[platform]/tools/console/tutorial/bindui/index.mdx @@ -4,28 +4,16 @@ export const meta = { title: 'Bind UI to data', description: 'Figma to React code with Amplify Studio', platforms: [ - 'android', - 'angular', - 'flutter', 'javascript', 'nextjs', - 'react', - 'react-native', - 'swift', - 'vue' + 'react' ], canonicalObjects: [ { platforms: [ - 'nextjs', - 'angular', 'javascript', - 'swift', - 'vue', - 'android', - 'react-native', - 'react', - 'flutter' + 'nextjs', + 'react' ], canonicalPath: '/javascript/tools/console/tutorial/bindui/' } diff --git a/src/pages/[platform]/tools/console/tutorial/buildui/index.mdx b/src/pages/[platform]/tools/console/tutorial/buildui/index.mdx index 772c259e125..1bb1b3d4093 100644 --- a/src/pages/[platform]/tools/console/tutorial/buildui/index.mdx +++ b/src/pages/[platform]/tools/console/tutorial/buildui/index.mdx @@ -4,28 +4,16 @@ export const meta = { title: 'Build UI', description: 'Figma to React code with Amplify Studio', platforms: [ - 'android', - 'angular', - 'flutter', 'javascript', 'nextjs', - 'react', - 'react-native', - 'swift', - 'vue' + 'react' ], canonicalObjects: [ { platforms: [ - 'react-native', - 'flutter', - 'swift', 'javascript', - 'react', 'nextjs', - 'vue', - 'angular', - 'android' + 'react' ], canonicalPath: '/javascript/tools/console/tutorial/buildui/' } diff --git a/src/pages/[platform]/tools/console/tutorial/code/index.mdx b/src/pages/[platform]/tools/console/tutorial/code/index.mdx index ef266e8f3e7..35c0d282671 100644 --- a/src/pages/[platform]/tools/console/tutorial/code/index.mdx +++ b/src/pages/[platform]/tools/console/tutorial/code/index.mdx @@ -4,28 +4,16 @@ export const meta = { title: 'Write React code', description: 'Create a database and GraphQL API.', platforms: [ - 'android', - 'angular', - 'flutter', 'javascript', 'nextjs', - 'react', - 'react-native', - 'swift', - 'vue' + 'react' ], canonicalObjects: [ { platforms: [ - 'angular', 'javascript', - 'vue', - 'android', - 'react', - 'swift', 'nextjs', - 'flutter', - 'react-native' + 'react' ], canonicalPath: '/javascript/tools/console/tutorial/code/' } diff --git a/src/pages/[platform]/tools/console/tutorial/collections/index.mdx b/src/pages/[platform]/tools/console/tutorial/collections/index.mdx index 38612a81db8..9128a9c863b 100644 --- a/src/pages/[platform]/tools/console/tutorial/collections/index.mdx +++ b/src/pages/[platform]/tools/console/tutorial/collections/index.mdx @@ -4,28 +4,16 @@ export const meta = { title: 'Collections', description: 'Figma to React code with Amplify Studio', platforms: [ - 'android', - 'angular', - 'flutter', 'javascript', 'nextjs', - 'react', - 'react-native', - 'swift', - 'vue' + 'react' ], canonicalObjects: [ { platforms: [ - 'angular', - 'swift', - 'vue', - 'flutter', - 'react', - 'nextjs', - 'android', 'javascript', - 'react-native' + 'nextjs', + 'react' ], canonicalPath: '/javascript/tools/console/tutorial/collections/' } diff --git a/src/pages/[platform]/tools/console/tutorial/data/index.mdx b/src/pages/[platform]/tools/console/tutorial/data/index.mdx index 286ff087cff..1ebb0a6bce2 100644 --- a/src/pages/[platform]/tools/console/tutorial/data/index.mdx +++ b/src/pages/[platform]/tools/console/tutorial/data/index.mdx @@ -4,27 +4,15 @@ export const meta = { title: 'Model database', description: 'Create a database and GraphQL API.', platforms: [ - 'android', - 'angular', - 'flutter', 'javascript', 'nextjs', - 'react', - 'react-native', - 'swift', - 'vue' + 'react' ], canonicalObjects: [ { platforms: [ - 'react-native', 'javascript', - 'android', - 'flutter', - 'swift', - 'angular', 'nextjs', - 'vue', 'react' ], canonicalPath: '/javascript/tools/console/tutorial/data/' diff --git a/src/pages/[platform]/tools/console/tutorial/index.mdx b/src/pages/[platform]/tools/console/tutorial/index.mdx index 700a681ccea..11a4befca5d 100644 --- a/src/pages/[platform]/tools/console/tutorial/index.mdx +++ b/src/pages/[platform]/tools/console/tutorial/index.mdx @@ -5,28 +5,16 @@ export const meta = { title: 'Tutorial', description: 'Tutorial', platforms: [ - 'android', - 'angular', - 'flutter', 'javascript', 'nextjs', - 'react', - 'react-native', - 'swift', - 'vue' + 'react' ], route: '/[platform]/tools/console/tutorial', canonicalObjects: [ { platforms: [ - 'angular', - 'react-native', - 'android', - 'flutter', 'react', - 'swift', 'javascript', - 'vue', 'nextjs' ], canonicalPath: '/javascript/tools/console/tutorial/' From 28ddcfdc424958f339ba05a3c22a3d48fb98a1d6 Mon Sep 17 00:00:00 2001 From: Edward Foyle Date: Wed, 6 Mar 2024 13:22:42 -0800 Subject: [PATCH 44/91] update gen2 storage and function docs (#7009) * update gen2 storage and function docs * add resource access links and example * revisions, update code snippet titles --- .../gen2/build-a-backend/functions/index.mdx | 196 ++++++++++++-- .../gen2/build-a-backend/storage/index.mdx | 239 ++++++++++++++++-- 2 files changed, 395 insertions(+), 40 deletions(-) diff --git a/src/pages/gen2/build-a-backend/functions/index.mdx b/src/pages/gen2/build-a-backend/functions/index.mdx index a0ee1185ff8..526abaf6dbf 100644 --- a/src/pages/gen2/build-a-backend/functions/index.mdx +++ b/src/pages/gen2/build-a-backend/functions/index.mdx @@ -20,8 +20,7 @@ export function getStaticProps(context) { To create a function, start by creating a file `amplify/functions/my-demo-function/resource.ts`. Paste the following content into the file. -```ts -// amplify/functions/my-demo-function/resource.ts +```ts title="amplify/functions/my-demo-function/resource.ts" import { defineFunction } from '@aws-amplify/backend'; export const myDemoFunction = defineFunction({ @@ -34,9 +33,7 @@ export const myDemoFunction = defineFunction({ Next, create `amplify/functions/my-demo-function/handler.ts`. This is where your function code will go. -```ts -// amplify/functions/my-demo-function/handler.ts - +```ts title="amplify/functions/my-demo-function/handler.ts" export const handler = async (event) => { // your function code goes here return 'You made a function!'; @@ -47,26 +44,196 @@ The handler file _must_ export an async function named "handler". This is the en Lastly, this function needs to be added to your backend. -```ts -// amplify/backend.ts +```ts title="amplify/backend.ts" import { defineBackend } from '@aws-amplify/backend'; +// highlight-next-line import { myDemoFunction } from './functions/my-demo-function/resource'; defineBackend({ + // highlight-next-line myDemoFunction }); ``` Now when you run `npx amplify sandbox` or deploy your app on Amplify, it will include your backend function. However, just defining a function doesn't do a whole lot. We need a way to invoke the function based on some event or request. Let's take a look at some examples to see how we can do this. +## Environment variables + +Environment variables can be configured in `defineFunction` using the `environment` property. + +```ts title="amplify/functions/my-demo-function/resource.ts" +import { defineFunction } from '@aws-amplify/backend'; + +export const myDemoFunction = defineFunction({ + environment: { + ENV_VAR_NAME: 'someValueHere' + } +}); +``` + +Any environment variables specified here will be available to the function at runtime. + +Some environment variables are constant across all branches and deployments. But many environment values differ between deployment environments. [Branch-specific environment variables can be configured for Amplify hosting deployments](gen2/deploy-and-host/fullstack-branching/secrets-and-vars/#set-environment-variables). + +Suppose you created a branch-specific environment variable in hosting called "API_ENDPOINT" which had a different value for your "staging" vs "prod" branch. If you wanted that value to be available to your function you can pass it to the function using + +```ts title="amplify/functions/my-demo-function/resource.ts" +export const myDemoFunction = defineFunction({ + environment: { + API_ENDPOINT: process.env.API_ENDPOINT + } +}); +``` + +### Accessing environment variables + +Within your function handler, you can access environment variables using the normal `process.env` global object provided by the Node runtime. However, this does not make it easy to discover what environment variables will be available at runtime. Amplify generates an `env` symbol that can be used in your function handler and provides typings for all variables that will be available at runtime. Copy the following code to use it. + +```ts title="amplify/functions/my-demo-function/handler.ts" +// highlight-next-line +import { env } from '@env/my-demo-function'; // the import is '@env/' + +export const handler = async (event) => { + env. // the env object has intellisense for all environment variables that are available to the function + return 'You made a function!'; +}; +``` + +## Secret access + +Sometimes it is necessary to provide a secret value to a function. For example, it may need a database password or an API key to perform some business use case. Environment variables should NOT be used for this because environment variable values are included in plaintext in the function configuration. Instead, secret access can be used. + +Before using a secret in a function, you need to [define a secret](gen2/deploy-and-host/fullstack-branching/secrets-and-vars/#set-secrets). After you have defined a secret, you can reference it in your function config. + +```ts title="amplify/functions/my-demo-function/resource.ts" +import { defineFunction, secret } from '@aws-amplify/backend'; + +export const myDemoFunction = defineFunction({ + environment: { + API_KEY: secret('myApiKey') // this assumes you created a secret named "myApiKey" + } +}); +``` + +You can use this secret value at runtime in your function the same as any other environment variable. However, you will notice that the value of the environment variable is not stored as part of the function configuration. Instead, the value is fetched when your function runs and is provided in memory. + +```ts title="amplify/functions/my-demo-function/handler.ts" +import { env } from '@env/my-demo-function'; + +export const handler = async (event) => { + env.API_KEY; // this is the value of secret named "myApiKey" + return 'You made a function!'; +}; +``` + +## Resource access + +When you grant a function access to another resource in your Amplify backend ([such as granting access to storage](../storage/#resource-access)), that will configure environment variables for that function to make SDK calls to the AWS services it has access to. Those environment variables are typed and available as part of the `env` object. + +Example `defineStorage` that grants myDemoFunction access to files in `/foo/*`. + +```ts title="amplify/storage/resource.ts" +import { myDemoFunction } from '../functions/my-demo-function/resource'; + +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/*': [allow.resource(demoFunction).to(['read', 'write', 'delete'])] + }) +}); +``` + +This access definition will add the environment variable `myProjectFiles_BUCKET_NAME` to the function. This environment variable can be accessed on the `env` object. + +Here's an example of how it can be used to upload some content to S3. + +```ts title="amplify/functions/my-demo-function/handler.ts" +import { env } from '@env/my-demo-function'; +import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; + +const s3Client = new S3Client(); + +export const handler = async (event) => { + await s3Client.send( + new PutObjectCommand({ + Bucket: env.myProjectFiles_BUCKET_NAME, + Key: 'foo/someFile.txt', + Body: 'this is an example' + }) + ); +}; +``` + +## Additional configuration + +`defineFunction` comes out-of-the-box with sensible but minimal defaults. The following options are provided to tweak the function configuration. + +### `timeoutSeconds` + +By default, functions will time out after 3 seconds. This can be configured to any whole number of seconds up to 15 minutes. + +```ts title="amplify/functions/my-demo-function/resource.ts" +export const myDemoFunction = defineFunction({ + // highlight-next-line + timeoutSeconds: 60 // 1 minute timeout +}); +``` + +### `memoryMB` + +By default, functions have 512 MB of memory allocated to them. This can be configured from 128 MB up to 10240 MB. Note that this can increase the cost of executing the function. For more pricing information see [here](https://aws.amazon.com/lambda/pricing/). + +```ts title="amplify/functions/my-demo-function/resource.ts" +export const myDemoFunction = defineFunction({ + // highlight-next-line + memoryMB: 256 // allocate 256 MB of memory to the function. +}); +``` + +### `runtime` + +Currently, only Node runtimes are supported by `defineFunction`. However, you can change the Node version that is used by the function. The default is the oldest Node LTS version that is supported by AWS Lambda (currently Node 18). + +If you which to use an older version of Node, keep an eye on the [Lambda Node version deprecation schedule](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). As Lambda removes support for old Node versions, you will have to update to newer supported versions. + +```ts title="amplify/functions/my-demo-function/resource.ts" +export const myDemoFunction = defineFunction({ + runtime: 20 // use Node 20 +}); +``` + +### `entry` + +By default, Amplify will look for your function handler in a file called `handler.ts` in the same directory as the file where `defineFunction` is called. To point to a different handler location, specify an `entry` value. + +```ts title="amplify/functions/my-demo-function/resource.ts" +export const myDemoFunction = defineFunction({ + entry: './path/to/handler.ts' // this path should either be absolute or relative to the current file +}); +``` + +### `name` + +By default, functions are named based on the directory the `defineFunction` call is placed in. In the above example, defining the function in `amplify/functions/my-demo-function/resource.ts` will cause the function to be named `my-demo-function` by default. + +If an entry is specified, then the name defaults to the basename of the entry path. For example, an `entry` of `./signup-trigger-handler.ts` would cause the function name to default to `signup-trigger-handler`. + +This optional property can be used to explicitly set the name of the function. + +```ts title="amplify/functions/my-demo-function/resource.ts" +export const myDemoFunction = defineFunction({ + entry: './demo-function-handler.ts', + name: 'overrideName' // explicitly set the name to override the default naming behavior +}); +``` + ## Example - Create a Function trigger for Auth Auth has several events that can trigger functions to perform custom sign up, sign in and other tasks. In this example you will configure a "preSignUp" trigger. First, in your auth definition, add the following: -```ts -// amplify/auth/resource.ts +```ts title="amplify/auth/resource.ts" export const auth = defineAuth({ loginWith: { @@ -83,8 +250,7 @@ export const auth = defineAuth({ Then create the function definition at `amplify/auth/pre-sign-up-handler.ts`. -```ts -// amplify/auth/pre-sign-up-handler.ts +```ts title="amplify/auth/pre-sign-up-handler.ts" import type { PreSignUpTriggerHandler } from 'aws-lambda'; export const handler: PreSignUpTriggerHandler = async (event) => { @@ -111,11 +277,3 @@ See [Custom business logic](/gen2/build-a-backend/data/custom-business-logic/) ## Example - Use a function as a custom authorizer in your data model See [Custom data access patterns](/gen2/build-a-backend/data/customize-authz/custom-data-access-patterns/) - -### Scheduled jobs - -We're actively working on a feature to trigger functions on a recurring schedule. - -### More function configuration - -You'll soon be able to customize function configuration like memory size, timeout, environment variables, access secret values and more. Keep your `@aws-amplify/backend` package up to date and come back to see what's new. diff --git a/src/pages/gen2/build-a-backend/storage/index.mdx b/src/pages/gen2/build-a-backend/storage/index.mdx index 89142f4749c..574a1b87cae 100644 --- a/src/pages/gen2/build-a-backend/storage/index.mdx +++ b/src/pages/gen2/build-a-backend/storage/index.mdx @@ -11,44 +11,241 @@ export function getStaticProps(context) { }; } - + -**Coming soon:** The Storage experience for Amplify (Gen 2) is still under development. You can add Amazon S3 to your Amplify (Gen 2) project with the AWS CDK. +**Under active development:** The Storage experience for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose -To use Storage today, you must use the [AWS Cloud Development Kit (CDK)](https://docs.aws.amazon.com/cdk/), which is installed by default as part of the [`create-amplify`](https://www.npmjs.com/package/create-amplify) workflow. +Adding storage to your Amplify backend enables uploading and downloading files. To get started using storage, create a file `amplify/storage/resource.ts`. Paste the following content into the file. -To get started, modify your project's `backend.ts` file to create an Amazon S3 bucket and grant Amplify-generated resources access to read and write: +```ts title="amplify/storage/resource.ts" +import { defineStorage } from '@aws-amplify/backend'; + +export const storage = defineStorage({ + name: 'myProjectFiles' +}); +``` + +Then include storage in your backend definition. ```ts title="amplify/backend.ts" -// amplify/backend.ts -import * as s3 from 'aws-cdk-lib/aws-s3'; import { defineBackend } from '@aws-amplify/backend'; -import { auth } from './auth/resource.js'; -import { data } from './data/resource.js'; +import { auth } from './auth/resource'; +// highlight-next-line +import { storage } from './storage/resource'; -const backend = defineBackend({ +defineBackend({ auth, - data + // highlight-next-line + storage }); +``` + +Now when you run `npx amplify sandbox` or deploy your app on Amplify, it will configure AWS resources for file upload and download. + +Before files can be accessed by your application, you must configure storage access rules. + +To learn how to use storage in your frontend, see docs on [uploading files](../../../javascript/build-a-backend/storage/upload) or [downloading files](../../../javascript/build-a-backend/storage/download/). + +## Storage access -// create the bucket and its stack -const bucketStack = backend.createStack('BucketStack'); -const bucket = new s3.Bucket(bucketStack, 'Bucket', { - blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL +By default, no users or other project resources have access to any files in storage. Access must be explicitly granted within `defineStorage` using the `access` callback. + +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/some/path/*': [ + // access rules that apply to all files within "/some/path/*" go here + ], + '/another/path/*': [ + // access rules that apply to all files within "/another/path/*" go here + ] + }) }); +``` + +The access callback returns an object where each key in the object is a file prefix and each value in the object is a list of access rules that apply to that prefix. The following sections enumerate the types of access rules that can be applied. + +### Authenticated user access + +To grant all authenticated (signed in) users of your application read access to files that start with `/foo/*`, use the following `access` configuration. -// allow any authenticated user to read and write to the bucket -const authRole = backend.auth.resources.authenticatedUserIamRole; -bucket.grantReadWrite(authRole); +Note that your backend must include `defineAuth` in order to use this access rule. -// allow any guest (unauthenticated) user to read from the bucket -const unauthRole = backend.auth.resources.unauthenticatedUserIamRole; -bucket.grantRead(unauthRole); +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/*': [allow.authenticated.to(['read'])] // additional actions such as "write" and "delete" can be specified depending on your use case + }) +}); ``` -Finally, run `npx amplify sandbox` to create your first Amazon S3 bucket! +### Guest user access + +To grant all guest (not signed in) users of your application read access to files that start with `/foo/*`, use the following `access` config. + +Note that your backend must include `defineAuth` in order to use this access rule. + +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/*': [allow.guest.to(['read'])] // additional actions such as "write" and "delete" can be specified depending on your use case + }) +}); +``` + +### User group access + +If you have configured user groups in `defineAuth`, you can scope storage access to specific groups. Suppose you have a `defineAuth` config with `admin` and `auditor` groups. + +```ts title="amplify/auth/resource.ts" +import { defineAuth } from '@aws-amplify/backend'; + +export const auth = defineAuth({ + loginWith: { + email: true + }, + groups: ['auditor', 'admin'] +}); +``` + +With the following `access` definition, you can configure permissions such that auditors have readonly permissions to `/foo/*` while admin has full permissions. + +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/*': [ + allow.group('auditor').to(['read']), + allow.group('admin').to(['read', 'write', 'delete']) + ] + }) +}); +``` + +### Owner-based access + +Access to files with a certain prefix can be scoped down to individual authenticated users. To do this, a placeholder token is used in the storage path which will be substituted with the user identity when uploading or downloading files. The access rule will only allow a user to upload or download files with their specific identity string. + +Note that your backend must include `defineAuth` in order to use this access rule. + +The following policy would allow authenticated users full access to files with a prefix that matches their identity id. + +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/{entity_id}/*': [ + // {entity_id} is the token that is replaced with the user identity id + allow.entity('identity').to(['read', 'write', 'delete']) + ] + }) +}); +``` + +A user with identity id "123" would be able to perform read/write/delete operations on files within `/foo/123/*` and would not be able to perform actions on files with any other prefix. Likewise, a user with identity id "ABC" would be able to perform read/write/delete operation on files only within `/foo/ABC/*`. In this way, each user can be granted access to a "private storage location" that is not accessible to any other user. + +It may be desireable for a file owner to be able to write and delete files in their private location but allow anyone to read from that location. For example, profile pictures should be readable by anyone, but only the owner can modify them. This use case can be configured with the following definition. + +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/{entity_id}/*': [ + allow.entity('identity').to(['read', 'write', 'delete']), + allow.guest.to(['read']), + allow.authenticated.to(['read']) + ] + }) +}); +``` + +When a non-id-based rule is applied to a path with the `{entity_id}` token, the token is replaced with a wildcard (`*`). This means that the access will apply to files uploaded by _any_ user. In the above policy, write and delete is scoped to just the owner, but read is allowed for guest and authenticated users for any file within `/foo/*/*`. + +### Grant function access + +In addition to granting application users access to storage files, you may also want to grant a backend function access to storage files. This could be used to enable a use case like resizing images, or automatically deleting old files. The following configuration is used to define function access. + +```ts title="amplify/storage/resource.ts" +import { defineStorage, defineFunction } from '@aws-amplify/backend'; + +const demoFunction = defineFunction({}); + +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/*': [allow.resource(demoFunction).to(['read', 'write', 'delete'])] + }) +}); +``` + +This would grant the function `demoFunction` the ability to read write and delete files within `/foo/*`. + +When a function is granted access to storage, it also receives an environment variable that contains the name of the S3 bucket configured by storage. This environment variable can be used in the function to make SDK calls to the storage bucket. The environment variable is named `_BUCKET_NAME`. In the above example, it would be named `myProjectFiles_BUCKET_NAME`. + +[Learn more about function resource access environment variables](../functions/#resource-access) + +### Access definition limitations + +There are some limitations on the types of prefixes that can be specified in the storage access definition. + +1. All paths are treated as prefixes from the storage root. As such all paths must start with `/` and end with `/*` to make this explicit. +2. Only one level of nesting is allowed. For example, you can define access controls on `/foo/*` and `/foo/bar/*` but not on `/foo/bar/baz/*` because that path has 2 other prefixes. +3. Wildcards cannot conflict with the `{entity_id}` token. For example, you cannot have both `/foo/*` and `/foo/{entity_id}/*` defined because the wildcard in the first path conflicts with the `{entity_id}` token in the second path. +4. A path cannot be a prefix of another path with an `{entity_id}` token. For example `/foo/*` and `/foo/bar/{entity_id}/*` is not allowed. + +### Prefix behavior + +When one path is a subpath of another, the permissions on the subpath _always override_ the permissions from the parent path. Permissions are not "inherited" from a parent path. Consider the following access definition example. + +```ts +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/foo/*': [allow.authenticated.to(['read', 'write', 'delete'])], + '/foo/bar/*': [allow.guest.to(['read'])], + '/foo/baz/*': [allow.authenticated.to(['read'])], + '/other/*': [ + allow.guest.to(['read']), + allow.authenticated.to(['read', 'write']) + ] + }) +}); +``` + +The access control matrix for this configuration is + +| | /foo/\* | /foo/bar/\* | /foo/baz/\* | /other/\* | +| --- | --- | --- | --- | --- | +| **Authenticated Users** | read, write, delete | NONE | read | read, write | +| **Guest users** | NONE | read | NONE | read | + +Authenticated users have access to read, write, and delete everything under `/foo/*` EXCEPT `/foo/bar/*` and `/foo/baz/*`. For those subpaths, the scoped down access overrides the access granted on the parent `/foo/*` + +### Configuring Amplify Gen 1-equivalent access patterns + +To configure `defineStorage` in Amplify Gen 2 to behave the same way as the storage category in Gen 1, the following definition can be used. + +```ts title="amplify/storage/resource.ts" +export const storage = defineStorage({ + name: 'myProjectFiles', + access: (allow) => ({ + '/public/*': [ + allow.guest.to(['read']) + allow.authenticated.to(['read', 'write', 'delete']), + ], + '/protected/{entity_id}/*': [ + allow.authenticated.to(['read']), + allow.entity('identity').to(['read', 'write', 'delete']) + ], + '/private/{entity_id}/*': [allow.entity('identity').to(['read', 'write', 'delete'])] + }) +}); +``` ### Next steps From 5cd0ec322ffa1588d02e06161bf298b76f320a36 Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:00:43 -0800 Subject: [PATCH 45/91] Update CODEOWNERS (#7014) --- .github/CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4f6c6de7687..6983aafa2f8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -84,6 +84,9 @@ #Utilities /src/**/**/utilities @josefaidt @aws-amplify/documentation-team +#Gen2 +/src/pages/gen2 @ykethan @jay2113 @chrisbonifacio @aspittel @renebrandel @swaminator @dbanksdesign @josefaidt @ErikCH @arundna @hdworld11 + #Docs Engineering /src/components @aws-amplify/documentation-team /src/constants @aws-amplify/documentation-team From e4f697da39f5c1567c1c5182dfa99c1bf0b08d55 Mon Sep 17 00:00:00 2001 From: Adi <34354177+the1adi@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:34:41 -0500 Subject: [PATCH 46/91] updated steps for extending components in code (#6827) Co-authored-by: Aditya Shahani --- .../[platform]/build-ui/uibuilder/override/index.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/[platform]/build-ui/uibuilder/override/index.mdx b/src/pages/[platform]/build-ui/uibuilder/override/index.mdx index 9db1b0f671c..3916e82bbab 100644 --- a/src/pages/[platform]/build-ui/uibuilder/override/index.mdx +++ b/src/pages/[platform]/build-ui/uibuilder/override/index.mdx @@ -204,12 +204,12 @@ In the example above, you can add your own custom business logic, when the custo You can't directly customize all generated component code, as changes will be overwritten on the next `amplify pull`. However, the following workaround is available if you want to take control of component modifications. -1. Duplicate the generated JSX and TS file inside `ui-components` (e.g. `Ampligram`) -2. Change the name of the files to something else (e.g. `Ampligram2`) and update the function names to match as well. -3. Update `index.js` to include the new export (e.g. `export { default as Ampligram2 } from "./Ampligram2";`) +1. Duplicate the generated JSX and TS file from `ui-components` to a new folder (e.g. `Ampligram`) +2. Change the name of the file to something else (e.g. `Ampligram2`) and update the function names to match as well. +3. Create a new 'index.js' file in the new folder to include the new export (e.g. `export { default as Ampligram2 } from "./Ampligram2";`) 4. Import the duplicated component wherever you want. -The next `amplify pull` will not overwrite this new file. +The next `amplify pull` will not overwrite the new files. ## Example use cases From 941a617b13c4755fac9608b70a1b4d4866d07ed8 Mon Sep 17 00:00:00 2001 From: Rene Brandel <4989523+renebrandel@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:34:47 -0500 Subject: [PATCH 47/91] Custom business logic support as of March @beta (#7019) * Enhanced custom business logic DX * addressed tim's comments * Added custom subscription configuration worfklow * removed in-development areas * added titles * added links for deep dive material * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx Co-authored-by: josef * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx Co-authored-by: josef * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx Co-authored-by: Kethan sai * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx Co-authored-by: Kethan sai * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx Co-authored-by: josef * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx * added client method for clarity. * Update src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx Co-authored-by: Kethan sai * Apply suggestions from code review Co-authored-by: josef --------- Co-authored-by: josef Co-authored-by: Kethan sai --- .../data/custom-business-logic/index.mdx | 256 +++++++++++++----- 1 file changed, 187 insertions(+), 69 deletions(-) diff --git a/src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx b/src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx index 13f6d136bed..734430db356 100644 --- a/src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx +++ b/src/pages/gen2/build-a-backend/data/custom-business-logic/index.mdx @@ -1,27 +1,20 @@ export const meta = { - title: 'Add custom business logic (Experimental)', + title: 'Add custom queries and mutations', description: - 'Customize your business logic for queries, mutations, and subscriptions.' + 'Customize your business logic for queries and mutations.' }; -export function getStaticProps(context) { +export function getStaticProps() { return { props: { meta } }; - } - - -**Coming soon:** The custom business logic experience in Amplify (Gen 2) is still under development. Expect the API surface to change dramatically during the Gen-2 preview period. If you have any feedback on the desired developer experience, please [file a GitHub issue](https://github.com/aws-amplify/amplify-backend). - - - -The default GraphQL API provides a solid foundation for querying, mutating, and fetching data. Even so, you may need additional customization to tailor it to your app's specific requirements around data handling, response formatting, and more. +The `a.model()` data model provides a solid foundation for querying, mutating, and fetching data. However, you may need additional customizations to meet specific requirements around custom API requests, response formatting, and/or fetching from external data sources. -In the following sections, we walk through the three steps to create a custom mutation or query: +In the following sections, we walk through the three steps to create a custom query or mutation: 1. Define a custom query or mutation 2. Configure custom business logic handler code @@ -79,7 +72,7 @@ import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; const schema = a.schema({ // 1. Define your return type as a custom type or model - Post: a.customType({ + Post: a.model({ id: a.id(), content: a.string(), likes: a.integer() @@ -90,7 +83,7 @@ const schema = a.schema({ .mutation() // arguments that this query accepts .arguments({ - content: a.string() + postId: a.string() }) // return type of the query .returns(a.ref('Post')) @@ -111,10 +104,15 @@ export const data = defineData({ ## Step 2 - Configure custom business logic handler code -After your query or mutation is defined, you need to author your custom business logic in a function. In your **amplify/data/** folder, create a **echo-handler.ts** file. +After your query or mutation is defined, you need to author your custom business logic. You can either define it in a [function](/gen2/build-a-backend/functions/) or using a [custom resolver powered by AppSync JavaScript resolver](https://docs.aws.amazon.com/appsync/latest/devguide/tutorials-js.html). -```ts -// amplify/data/echo-handler.ts + + + + +In your `amplify/data/echo-handler/` folder, create a `handler.ts` file. + +```ts title="amplify/data/echo-handler.ts" import type { AppSyncResolverHandler } from 'aws-lambda'; // types imported from @types/aws-lambda type ResolverArgs = { content: string }; @@ -133,10 +131,9 @@ export const handler: AppSyncResolverHandler< }; ``` -In your **amplify/data/resource.ts** file, define the function using `defineFunction` and then reference the function with your query or mutation using the `.function()` modifier along with the `functions` map of `defineData`. +In your `amplify/data/resource.ts` file, define the function using `defineFunction` and then reference the function with your query or mutation using the `.function()` modifier along with the `functions` map of `defineData`. -```ts -// amplify/data/resource.ts +```ts title="amplify/data/resource.ts" import { type ClientSchema, a, @@ -144,6 +141,10 @@ import { defineFunction // Step 1 - Import "defineFunction" to create new functions } from '@aws-amplify/backend'; +const echoHandler = defineFunction({ + entry: './echo-handler/handler.ts' +}) + const schema = a.schema({ EchoResponse: a.customType({ content: a.string(), @@ -155,9 +156,6 @@ const schema = a.schema({ .arguments({ content: a.string() }) .returns(a.ref('EchoResponse')) .authorization([a.allow.public()]) - // Step 2 - Define a function key ("echoHandler") that will be used - // to map this query to the corresponding functions provided to - // defineData(...) .function('echoHandler') }); @@ -169,72 +167,192 @@ export const data = defineData({ defaultAuthorizationMode: 'apiKey', apiKeyAuthorizationMode: { expiresInDays: 30 - } + }, }, functions: { - // Step 3 - Map the function key to the function definition - echoHandler: defineFunction({ - entry: './echo-handler.ts' - }) + 'echoHandler': echoHandler } }); ``` -## Step 3 - Invoke the custom query or mutation + + - +Custom resolvers work on a "request/response" basis, you choose a data source, map your request to the data source's input parameters, and then map the data source's response back to the query/mutation's return type. Custom resolvers provide the benefit of no cold starts, less infrastructure to manage, and no additional charge for Lambda function invocations. Review [Choosing between custom resolver and function](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html#choosing-data-source). -**Coming soon:** The following example requires you to manually generate GraphQL client code as an escape hatch to invoke the custom query or mutation. Amplify Data is built with GraphQL under the hood. While you're not expected to know GraphQL or have to interface with GraphQL directly when Amplify (Gen 2) becomes generally available, it is helpful to understand the [underlying GraphQL concepts](https://graphql.org) that enable Amplify Data's functionality. +In your `amplify/data/resource.ts` file, define a custom handler using `a.handler.custom`. + +```ts title="amplify/data/resource.ts" +import { + type ClientSchema, + a, + defineData, +} from '@aws-amplify/backend'; -
-For Gen 2's general availability, we plan on integrating this first class into the -Data client, so you can call a custom query or mutation with code, such as: +const schema = a.schema({ + Post: a.model({ + content: a.string(), + likes: a.integer() + .authorization([a.allow.private().to(['read'])]) + }).authorization([ + a.allow.owner(), + a.allow.private().to(['read']) + ]), -```ts -const response = await client.queries.echo({ content: 'hello' }); + likePost: a + .mutation() + .arguments({ postId: a.id() }) + .returns(a.ref('Post')) + .authorization([a.allow.private()]) + .handler(a.handler.custom({ + dataSource: a.ref('Post'), + entry: './increment-like.js' + })) +}); + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + apiKeyAuthorizationMode: { + expiresInDays: 30 + } + }, +}); ``` -
+```ts title="amplify/data/increment-like.js" +export function request(ctx) { + return { + operation: 'UpdateItem', + key: util.dynamodb.toMapValues({ id: ctx.args.postId}), + update: { + expression: 'ADD likes :plusOne', + expressionValues: { 'plusOne': { N: 1 } }, + } + } +} -In the preview of Amplify (Gen 2), you must first generate GraphQL client code for your custom queries and mutations and then pass them into the `client.graphql(...)` operation. In your terminal, run the following command: - -```bash -npx amplify generate graphql-client-code \ ---format graphql-codegen \ ---statement-target typescript \ ---type-target typescript \ ---out ./graphql \ ---stack -# You can find your stack name in your "npx amplify sandbox" -# console logs. It has the following format: -# amplify---sandbox- +export function response(ctx: Context) { + return ctx.result +} ``` -This should create the following set of GraphQL client helper code for you under **graphql/**: +By default, you'll be able to access any existing database tables (powered by Amazon DynamoDB) using `a.ref('MODEL_NAME')`. But you can also reference any other external data source from within your AWS account, by adding them to your backend definition. -- **API.ts** – All the type definitions for queries, mutations, and subscriptions -- **mutations.ts** – The GraphQL mutations from your API -- **queries.ts** – The GraphQL queries from your API -- **subscriptions.ts** – The GraphQL subscriptions from your API +The supported data sources are: +- Amazon DynamoDB +- AWS Lambda +- Amazon RDS databases with Data API +- Amazon EventBridge +- OpenSearch +- HTTP endpoints -To call the `echo` query from step 1, we need to import the `echo` function from **queries.ts** and pass it as a query to `client.graphql(...)`. +You can add these additional data sources via our `amplify/backend.ts` file: -```ts -import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource'; -import * as queries from '@/graphql/queries'; +```ts title="amplify/backend.ts" +import * as dynamoDb from 'aws-cdk-lib/aws-dynamodb' +import { defineBackend } from '@aws-amplify/backend'; +import { auth } from './auth/resource'; +import { data } from './data/resource'; + +export const backend = defineBackend({ + auth, + data, +}); + +const externalDataSourcesStack = backend.createStack("MyExternalDataSources") + +const externalTable = dynamoDb.Table.fromTableName(externalDataSourcesStack, "MyTable", "MyExternalTable") + +backend.data.addDynamoDbDataSource( + // highlight-next-line + "ExternalTableDataSource", + externalTable) +``` -const client = generateClient(); +In your schema you can then reference these additional data sources based on their name: -async function callEcho() { - const response = await client.graphql({ - query: echo, - variables: { - content: 'Echo me!' +```ts title="amplify/data/resource.ts" +import { + type ClientSchema, + a, + defineData, +} from '@aws-amplify/backend'; + +const schema = a.schema({ + Post: a.model({ + content: a.string(), + likes: a.integer() + .authorization([a.allow.private().to(['read'])]) + }).authorization([ + a.allow.owner(), + a.allow.private().to(['read']) + ]), + + likePost: a + .mutation() + .arguments({ postId: a.id() }) + .returns(a.ref('Post')) + .authorization([a.allow.private()]) + .handler(a.handler.custom({ + // highlight-next-line + dataSource: "ExternalTableDataSource", + entry: './increment-like.js' + })) +}); + +export type Schema = ClientSchema; + +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + apiKeyAuthorizationMode: { + expiresInDays: 30 } - }); + }, +}); +``` - console.log(response.data.echo?.content); - console.log(response.data.echo?.executionDuration); -} +
+
+ + + +The `.handler()` accepts an array of handlers that will run in a pipeline. For now, in the developer preview, you can only use `a.handler.custom()`. All other handlers, such as `a.handler.function()` are under active development. + + + +{/* + +All handlers must be of the same type. For example, you can't mix and match `a.handler.function` with `a.handler.custom` within a single `.handler()` modifier. + + */} + +## Step 3 - Invoke the custom query or mutation + +From your generated Data client, you can find all your custom queries and mutations under the `client.queries.` and `client.mutations.` APIs respectively. + + + + +```ts +const { data, errors } = await client.queries.echo({ + content: 'hello' +}); +``` + + + + +```ts +const { data, errors } = await client.mutations.likePost({ + postId: 'hello' +}); ``` + + + From ae8bad3ee1318b1551359de129ec06db5ee5d1d7 Mon Sep 17 00:00:00 2001 From: Edward Foyle Date: Thu, 7 Mar 2024 12:12:40 -0800 Subject: [PATCH 48/91] add storage trigger docs (#7020) * storage trigger docs * Apply suggestions from code review Co-authored-by: josef * add storage access actions --------- Co-authored-by: josef --- .../gen2/build-a-backend/functions/index.mdx | 6 +- .../gen2/build-a-backend/storage/index.mdx | 74 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/pages/gen2/build-a-backend/functions/index.mdx b/src/pages/gen2/build-a-backend/functions/index.mdx index 526abaf6dbf..a4ec14778fd 100644 --- a/src/pages/gen2/build-a-backend/functions/index.mdx +++ b/src/pages/gen2/build-a-backend/functions/index.mdx @@ -55,7 +55,7 @@ defineBackend({ }); ``` -Now when you run `npx amplify sandbox` or deploy your app on Amplify, it will include your backend function. However, just defining a function doesn't do a whole lot. We need a way to invoke the function based on some event or request. Let's take a look at some examples to see how we can do this. +Now when you run `npx amplify sandbox` or deploy your app on Amplify, it will include your backend function. See the [examples](#example---create-a-function-trigger-for-auth) below for connecting your functions to event sources. ## Environment variables @@ -277,3 +277,7 @@ See [Custom business logic](/gen2/build-a-backend/data/custom-business-logic/) ## Example - Use a function as a custom authorizer in your data model See [Custom data access patterns](/gen2/build-a-backend/data/customize-authz/custom-data-access-patterns/) + +## Example - Create a Function trigger for Storage + +See [Configure storage triggers](/gen2/build-a-backend/storage/#configure-storage-triggers) diff --git a/src/pages/gen2/build-a-backend/storage/index.mdx b/src/pages/gen2/build-a-backend/storage/index.mdx index 574a1b87cae..44a32b47c9c 100644 --- a/src/pages/gen2/build-a-backend/storage/index.mdx +++ b/src/pages/gen2/build-a-backend/storage/index.mdx @@ -226,6 +226,30 @@ The access control matrix for this configuration is Authenticated users have access to read, write, and delete everything under `/foo/*` EXCEPT `/foo/bar/*` and `/foo/baz/*`. For those subpaths, the scoped down access overrides the access granted on the parent `/foo/*` +### Available actions + +When you configure access to a particular storage prefix, you can scope the access to one or more CRUDL actions. + +#### `read` + +This is a convenience action that is equivalent to setting both `get` and `list` access. + +#### `get` + +This action maps to the [`s3:GetObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html) IAM action, scoped to the corresponding object prefix. + +#### `list` + +This action maps to the [`s3:ListBucket`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) IAM action, scoped to the corresponding object prefix. + +#### `write` + +This action maps to the [`s3:PutObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) IAM action, scoped to the corresponding object prefix. Note that this action grants the ability to both create new object and update existing ones. There is no way to scope access to only creating or only updating objects. + +#### `delete` + +This action maps to the [`s3:DeleteObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) IAM action, scoped to the corresponding object prefix. + ### Configuring Amplify Gen 1-equivalent access patterns To configure `defineStorage` in Amplify Gen 2 to behave the same way as the storage category in Gen 1, the following definition can be used. @@ -247,6 +271,56 @@ export const storage = defineStorage({ }); ``` +## Configure storage triggers + +Function triggers can be configured to enable event-based workflows when files are uploaded or deleted. To add a function trigger, modify the `defineStorage` configuration. + +First, in your storage definition, add the following: + +```ts title="amplify/storage/resource.ts" +export const auth = defineStorage({ + name: 'myProjectFiles', + // highlight-start + triggers: { + onUpload: defineFunction({ + entry: './on-upload-handler.ts' + }), + onDelete: defineFunction({ + entry: './on-delete-handler.ts' + }) + } + // highlight-end +}); +``` + +Then create the function definitions at `amplify/storage/on-upload-handler.ts` and `amplify/storage/on-delete-handler.ts`. + +```ts title="amplify/storage/on-upload-handler.ts" +import type { S3Handler } from 'aws-lambda'; + +export const handler: S3Handler = async (event) => { + const objectKeys = event.Records.map((record) => record.s3.object.key); + console.log(`Upload handler invoked for objects [${objectKeys.join(', ')}]`); +}; +``` + +```ts title="amplify/storage/on-delete-handler.ts" +import type { S3Handler } from 'aws-lambda'; + +export const handler: S3Handler = async (event) => { + const objectKeys = event.Records.map((record) => record.s3.object.key); + console.log(`Delete handler invoked for objects [${objectKeys.join(', ')}]`); +}; +``` + + + +**Note:** The `S3Handler` type comes from the [@types/aws-lambda](https://www.npmjs.com/package/@types/aws-lambda) NPM package. This package contains types for different kinds of Lambda handlers, events, and responses. + + + +Now, when you deploy your backend, these functions will be invoked whenever an object is uploaded or deleted from the bucket. + ### Next steps - [Learn more about `s3.Bucket`](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html) From 1dc24d6b0e260ea8d832d0cdec43a99ca9766b3f Mon Sep 17 00:00:00 2001 From: jacoblogan Date: Thu, 7 Mar 2024 14:31:36 -0700 Subject: [PATCH 49/91] =?UTF-8?q?update=20link=20checker=20to=20wait=20for?= =?UTF-8?q?=20each=20call=20to=20complete=20before=20making=20t=E2=80=A6?= =?UTF-8?q?=20(#6993)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update link checker to wait for each call to complete before making the next and remove # from urls being checked * update logging to show how many links found on each page * Update tasks/link-checker.js --------- Co-authored-by: Jacob Logan Co-authored-by: katiegoines Co-authored-by: Scott Rees <6165315+reesscot@users.noreply.github.com> --- tasks/link-checker.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tasks/link-checker.js b/tasks/link-checker.js index ff1cf1a6ab9..23a935105c0 100644 --- a/tasks/link-checker.js +++ b/tasks/link-checker.js @@ -1,5 +1,5 @@ -const puppeteer = require('puppeteer'); -const axios = require('axios'); +const puppeteer = require('puppeteer'); // eslint-disable-line +const axios = require('axios'); // eslint-disable-line const SITEMAP_URL = 'https://docs.amplify.aws/sitemap.xml'; const DOMAIN = 'https://docs.amplify.aws'; @@ -62,7 +62,6 @@ const retrieveLinks = async (siteMapUrls, visitedLinks, localDomain) => { let response = await page.goto(url, { waitUntil: 'domcontentloaded' }); await new Promise((r) => setTimeout(r, 100)); // localhost hangs on wait for idle so use a short timeout instead if (response && response.status() && response.status() === 200) { - console.log(`successfully visited ${url} to retrieve links`); visitedLinks[url] = true; const urlList = await page.evaluate(async (url) => { @@ -82,6 +81,10 @@ const retrieveLinks = async (siteMapUrls, visitedLinks, localDomain) => { return urls; }, url); + console.log( + `successfully visited ${url} to retrieve links. ${urlList.length} links found` + ); + urlList.forEach((link) => { if ( !CRAWLER_EXCEPTIONS.includes(link.url) && @@ -128,11 +131,9 @@ const linkChecker = async (localDomain) => { localDomain ); - let allPromises = []; - for (let i = 0; i < urlsToVisit.length; i++) { const link = urlsToVisit[i]; - let href = link.url; + let href = link.url.split('#')[0]; if (href.startsWith(GITHUB_CREATE_ISSUE_LINK)) { // remove query parameters from github new issue links href = href.split('?')[0]; @@ -163,11 +164,9 @@ const linkChecker = async (localDomain) => { } }); - allPromises.push(request); + await request; } - await Promise.all(allPromises); - console.log(statusCodes); console.log(brokenLinks); From 662bff38642da2811bae4d696dcbb267756f4e66 Mon Sep 17 00:00:00 2001 From: Kethan sai Date: Thu, 7 Mar 2024 17:19:51 -0500 Subject: [PATCH 50/91] custom examples (#6979) * custom examples --- src/directory/directory.mjs | 12 ++ .../add-aws-services/analytics/index.mdx | 76 ++++++++++ .../custom-resources/index.mdx | 18 ++- .../add-aws-services/geo/index.mdx | 97 +++++++++++++ .../in-app-messaging/index.mdx | 136 ++++++++++++++++++ .../add-aws-services/rest-api/index.mdx | 100 +++++++++++++ 6 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx create mode 100644 src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx create mode 100644 src/pages/gen2/build-a-backend/add-aws-services/in-app-messaging/index.mdx create mode 100644 src/pages/gen2/build-a-backend/add-aws-services/rest-api/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 29c53ead8fc..4d889e1d8c4 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -2109,6 +2109,18 @@ export const directory = { }, { path: 'src/pages/gen2/build-a-backend/add-aws-services/overriding-resources/index.mdx' + }, + { + path: 'src/pages/gen2/build-a-backend/add-aws-services/rest-api/index.mdx' + }, + { + path: 'src/pages/gen2/build-a-backend/add-aws-services/in-app-messaging/index.mdx' + }, + { + path: 'src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx' + }, + { + path: 'src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx' } ] } diff --git a/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx new file mode 100644 index 00000000000..96e0e428a31 --- /dev/null +++ b/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx @@ -0,0 +1,76 @@ +export const meta = { + title: 'Analytics', + description: 'Learn how to set Analytics resource powered by Pinpoint' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + + + +**Under active development:** The `addOutput` method for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose + + + +Amplify enables you to collect analytics data for your app. The Analytics category uses Amazon Cognito Identity pools to _identify_ users in your app. +The following is an example utilizing the [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) to create the an Analytics resource powered by [Amazon Pinpoint](https://aws.amazon.com/pinpoint/). + +```ts title="amplify/backend.ts" +import { Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { CfnApp } from "aws-cdk-lib/aws-pinpoint"; + +const backend = defineBackend({ + auth, + data, + // additional resources +}); + +const analyticsStack = backend.createStack("analytics-stack"); + +// create a Pinpoint app +const pinpoint = new CfnApp(analyticsStack, "Pinpoint", { + name: "myPinpointApp", +}); + +// create an IAM policy to allow interacting with Pinpoint +const pinpointPolicy = new Policy(analyticsStack, "PinpointPolicy", { + policyName: "PinpointPolicy", + statements: [ + new PolicyStatement({ + actions: ["mobiletargeting:UpdateEndpoint", "mobiletargeting:PutEvents"], + resources: [pinpoint.attrArn + "/*"], + }), + ], +}); + +// apply the policy to the authenticated and unauthenticated roles +backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(pinpointPolicy); +backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy(pinpointPolicy); + +// patch the custom Pinpoint resource to the expected output configuration +backend.addOutput({ + Analytics: { + AWSPinpoint: { + appId: pinpoint.ref, + region: Stack.of(pinpoint).region, + }, + }, +}); +``` + +### Initialize Analytics + +To initialize Analytics you need to configure Amplify with `Amplify.configure()` + +### Working with Analytics + +Refer to the [Analytics documentation](/react/build-a-backend/more-features/analytics/) to learn how to track events and user sessions in your app. + +### References + +[Amazon Pinpoint Construct Library](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pinpoint-readme.html) diff --git a/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx index 1248b1d7071..32240fa637b 100644 --- a/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx +++ b/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx @@ -103,19 +103,20 @@ type CustomNotificationsProps = { }; export class CustomNotifications extends Construct { + public readonly topic: sns.Topic; constructor(scope: Construct, id: string, props: CustomNotificationsProps) { super(scope, id); const { sourceAddress } = props; // Create SNS topic - const topic = new sns.Topic(this, 'NotificationTopic'); + this.topic = new sns.Topic(this, 'NotificationTopic'); // Create Lambda to publish messages to SNS topic const publisher = new lambda.NodejsFunction(this, 'Publisher', { entry: url.fileURLToPath(new URL('publisher.ts', import.meta.url)), environment: { - SNS_TOPIC_ARN: topic.topicArn + SNS_TOPIC_ARN: this.topic.topicArn }, runtime: Runtime.NODEJS_18_X }); @@ -130,10 +131,10 @@ export class CustomNotifications extends Construct { }); // Subscribe emailer Lambda to SNS topic - topic.addSubscription(new subscriptions.LambdaSubscription(emailer)); + this.topic.addSubscription(new subscriptions.LambdaSubscription(emailer)); // Allow publisher to publish to SNS topic - topic.grantPublish(publisher); + this.topic.grantPublish(publisher); } } ``` @@ -229,11 +230,18 @@ const backend = defineBackend({ data }); -new CustomNotifications( +const customNotifications = new CustomNotifications( backend.createStack('CustomNotifications'), 'CustomNotifications', { sourceAddress: 'sender@example.com' } ); + +backend.addOutput({ + custom: { + topicArn: customNotifications.topic.topicArn, + topicName: customNotifications.topic.topicName, + }, +}); ``` ## Community CDK resources diff --git a/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx new file mode 100644 index 00000000000..521ba0b0110 --- /dev/null +++ b/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx @@ -0,0 +1,97 @@ +export const meta = { + title: 'Geo', + description: 'Learn how to set up Geo resource powered by Amazon Location Services.' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + + + +**Under active development:** The `addOutput` method for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose + + + +Amplify provides APIs and map UI components for maps and location search for your web apps. The following is an example utilizing the [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) to create a Geo resource powered by [Amazon Location Services](https://aws.amazon.com/location/). + +```ts title="amplify/backend.ts" +import { CfnMap } from "aws-cdk-lib/aws-location"; + +const backend = defineBackend({ + auth, + data, + // additional resources +}); + +const geoStack = backend.createStack("geo-stack"); + +// create a location services map +const map = new CfnMap(geoStack, "Map", { + mapName: "myMap", + description: "Map", + configuration: { + style: "VectorEsriNavigation", + }, + pricingPlan: "RequestBasedUsage", + tags: [ + { + key: "name", + value: "myMap", + }, + ], +}); + +// create an IAM policy to allow interacting with geo resource +const myGeoPolicy = new Policy(geoStack, "AuthenticatedUserIamRolePolicy", { + policyName: "GeoPolicy", + statements: [ + new PolicyStatement({ + actions: [ + "geo:GetMapTile", + "geo:GetMapSprites", + "geo:GetMapGlyphs", + "geo:GetMapStyleDescriptor", + ], + resources: [map.attrArn], + }), + ], +}); + +// apply the policy to the authenticated and unauthenticated roles +backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(myGeoPolicy); +backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy(myGeoPolicy); + +// patch the custom map resource to the expected output configuration +backend.addOutput({ + geo: { + amazon_location_service: { + region: Stack.of(geoStack).region, + maps: { + items: { + [map.mapName]: { + style: "VectorEsriNavigation", + }, + }, + default: map.mapName, + }, + }, + }, +}); +``` + +### Initialize Geo + +To initialize Geo you need to configure Amplify with `Amplify.configure()` + +### Working with Maps + +To display a map in your React app, you can use the [Amplify React MapView component](https://ui.docs.amplify.aws/react/components/geo) or the [MapLibre GL](https://github.com/maplibre/maplibre-gl-js) with `maplibre-gl-js-amplify` libraries are required. Refer to the [Amplify Geo documentation](/react/build-a-backend/more-features/geo/maps/) for more information. + +### References + +[Location Construct Library](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_location-readme.html) diff --git a/src/pages/gen2/build-a-backend/add-aws-services/in-app-messaging/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/in-app-messaging/index.mdx new file mode 100644 index 00000000000..20f3d417550 --- /dev/null +++ b/src/pages/gen2/build-a-backend/add-aws-services/in-app-messaging/index.mdx @@ -0,0 +1,136 @@ +export const meta = { + title: 'In-App Messaging', + description: 'Learn how to set up In-App Messaging resource powered by Pinpoint' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + + + +**Under active development:** The `addOutput` method for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose + + + +Amplify allows interacting with In-App Messaging APIs, which enables you to send messages to your app users. In-App Messaging is a powerful tool to engage with your users and provide them with relevant information. The following is an example utilizing the [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) to create the In-App Messaging resource powered by [Amazon Pinpoint](https://aws.amazon.com/pinpoint/). + +```ts title="amplify/backend.ts" +import { + CfnApp, + CfnInAppTemplate, + CfnCampaign, + CfnSegment, +} from "aws-cdk-lib/aws-pinpoint"; +import { Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Stack } from "aws-cdk-lib"; + +const backend = defineBackend({ + auth, + data, + // additional resources +}); + +const inAppMessagingStack = backend.createStack("inAppMessaging-stack"); + +// create a Pinpoint app +const pinpoint = new CfnApp(inAppMessagingStack, "Pinpoint", { + name: "myPinpointApp", +}); + +// create a segment +const mySegment = new CfnSegment(inAppMessagingStack, "Segment", { + applicationId: pinpoint.ref, + name: "mySegment", +}); + +// create a campaign with event and in-app message template +new CfnCampaign(inAppMessagingStack, "MyCampaign", { + applicationId: pinpoint.ref, + name: "MyCampaign", + segmentId: mySegment.attrSegmentId, + schedule: { + // ensure the start and end time are in the future + startTime: "2024-02-23T14:39:34Z", + endTime: "2024-02-29T14:32:40Z", + frequency: "IN_APP_EVENT", + eventFilter: { + dimensions: { + eventType: { + dimensionType: "INCLUSIVE", + values: ["PURCHASE"], + }, + }, + filterType: "ENDPOINT", + }, + }, + + messageConfiguration: { + inAppMessage: { + layout: "TOP_BANNER", + content: [ + { + // define the content of the in-app message + bodyConfig: { + alignment: "CENTER", + body: "This is an example in-app message.", + textColor: "#FFFFFF", + }, + backgroundColor: "#000000", + headerConfig: { + alignment: "CENTER", + header: "Welcome!", + textColor: "#FFFFFF", + }, + // optionally, define buttons, images, etc. + }, + ], + }, + }, +}); + +//create an IAM policy to allow interacting with Pinpoint in-app messaging +const pinpointPolicy = new Policy(inAppMessagingStack, "PinpointPolicy", { + policyName: "PinpointPolicy", + statements: [ + new PolicyStatement({ + actions: ["mobiletargeting:GetInAppMessages"], + resources: [pinpoint.attrArn + "/*"], + }), + ], +}); + +// apply the policy to the authenticated and unauthenticated roles +backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(pinpointPolicy); +backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy(pinpointPolicy); + +// patch the custom Pinpoint resource to the expected output configuration +backend.addOutput({ + Notifications: { + InAppMessaging: { + AWSPinpoint: { + appId: pinpoint.ref, + region: Stack.of(pinpoint).region, + }, + }, + }, +}); +``` + + +### Initialize In-App Messaging + +To initialize In-App Messaging you need to configure Amplify with `Amplify.configure()` + + +### Working with In-App Messaging + +Refer to the [Amplify In-App Messaging documentation](/react/build-a-backend/more-features/in-app-messaging/) to learn how to send in-app messages to your app users. + +### References + +[Amazon Pinpoint Construct Library](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_pinpoint-readme.html) diff --git a/src/pages/gen2/build-a-backend/add-aws-services/rest-api/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/rest-api/index.mdx new file mode 100644 index 00000000000..a65686660e7 --- /dev/null +++ b/src/pages/gen2/build-a-backend/add-aws-services/rest-api/index.mdx @@ -0,0 +1,100 @@ +export const meta = { + title: 'REST API', + description: 'Learn how to set up REST API resource powered by Amazon API Gateway' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + + + +**Under active development:** The `addOutput` method for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose + + + +Amplify provides APIs to invoke your endpoints from your web apps. The following is an example utilizing the [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html) to add a REST API resource powered by [API Gateway](https://aws.amazon.com/api-gateway/). + +Create a [Function](/gen2/build-a-backend/functions) with the following. + +```ts title="amplify/functions/my-function/handler.ts" +import type { APIGatewayProxyHandlerV2 } from "aws-lambda"; + +export const handler: APIGatewayProxyHandlerV2 = async (event) => { + console.log("event", event); + return { + statusCode: 200, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": "*", + }, + body: JSON.stringify("Hello from myFunction!"), + }; +}; +``` + +Lets create API Gateway REST API with AWS Lambda proxy integration using AWS CDK and add the function. + +```ts title="amplify/backend.ts" +import { myFunction } from "./functions/my-function/resource"; +import { LambdaRestApi } from "aws-cdk-lib/aws-apigateway"; + +const backend = defineBackend({ + auth, + data, + myFunction +}); + +const apiGatewayStack = backend.createStack("apigateway-stack"); + +// create a REST API resource +const myAPI = new LambdaRestApi(apiGatewayStack, "MyApi", { + handler: backend.myFunction.resources.lambda, +}); + +// patch the custom REST API resource to the expected output configuration +backend.addOutput({ + custom: { + apiId: myAPI.restApiId, + apiEndpoint: myAPI.url, + apiName: myAPI.restApiName, + apiRegion: Stack.of(apiGatewayStack).region, + }, +}); +``` + +### Initialize REST API + +To initialize the REST API you need to configure Amplify with `Amplify.configure()` + +```tsx title="pages/index.tsx" +import { Amplify } from 'aws-amplify'; +import amplifyconfig from './src/amplifyconfiguration.json'; +Amplify.configure(amplifyconfig): + +const existingConfig = Amplify.getConfig(); + +Amplify.configure({ + ...existingConfig, + API: { + REST: { + [amplifyconfig.custom.apiName]: { + endpoint: amplifyconfig.custom.apiEndpoint, + region: amplifyconfig.custom.apiRegion, + }, + }, + }, +}); +``` + +### Working with REST API + +Refer to the [Amplify REST API documentation](/react/build-a-backend/restapi/) to learn how to work with REST API in your web app. + +### References + +[Amazon API Gateway Construct Library](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_apigateway-readme.html) From 54aaa73d9e5827a1d0fc72277c185380e9bfe27d Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Fri, 8 Mar 2024 09:32:06 -0800 Subject: [PATCH 51/91] fix: broken link, missing / at start (#7026) Co-authored-by: katiegoines --- src/pages/gen2/build-a-backend/functions/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/gen2/build-a-backend/functions/index.mdx b/src/pages/gen2/build-a-backend/functions/index.mdx index a4ec14778fd..9d351187387 100644 --- a/src/pages/gen2/build-a-backend/functions/index.mdx +++ b/src/pages/gen2/build-a-backend/functions/index.mdx @@ -73,7 +73,7 @@ export const myDemoFunction = defineFunction({ Any environment variables specified here will be available to the function at runtime. -Some environment variables are constant across all branches and deployments. But many environment values differ between deployment environments. [Branch-specific environment variables can be configured for Amplify hosting deployments](gen2/deploy-and-host/fullstack-branching/secrets-and-vars/#set-environment-variables). +Some environment variables are constant across all branches and deployments. But many environment values differ between deployment environments. [Branch-specific environment variables can be configured for Amplify hosting deployments](/gen2/deploy-and-host/fullstack-branching/secrets-and-vars/#set-environment-variables). Suppose you created a branch-specific environment variable in hosting called "API_ENDPOINT" which had a different value for your "staging" vs "prod" branch. If you wanted that value to be available to your function you can pass it to the function using @@ -103,7 +103,7 @@ export const handler = async (event) => { Sometimes it is necessary to provide a secret value to a function. For example, it may need a database password or an API key to perform some business use case. Environment variables should NOT be used for this because environment variable values are included in plaintext in the function configuration. Instead, secret access can be used. -Before using a secret in a function, you need to [define a secret](gen2/deploy-and-host/fullstack-branching/secrets-and-vars/#set-secrets). After you have defined a secret, you can reference it in your function config. +Before using a secret in a function, you need to [define a secret](/gen2/deploy-and-host/fullstack-branching/secrets-and-vars/#set-secrets). After you have defined a secret, you can reference it in your function config. ```ts title="amplify/functions/my-demo-function/resource.ts" import { defineFunction, secret } from '@aws-amplify/backend'; From 0d0e9aaa2389b927ec1e6bb05baac862855df4c5 Mon Sep 17 00:00:00 2001 From: Chris Bonifacio Date: Fri, 8 Mar 2024 16:21:28 -0500 Subject: [PATCH 52/91] validate and fix code snippets in Data section (#6975) --- .../connect-from-server-runtime/index.mdx | 57 +++++----- .../data/connect-to-API/index.mdx | 84 +++++++------- .../index.mdx | 7 +- .../custom-data-access-patterns/index.mdx | 14 +-- .../index.mdx | 16 ++- .../data/customize-authz/index.mdx | 18 +-- .../multi-user-data-access/index.mdx | 30 ++--- .../per-user-per-owner-data-access/index.mdx | 60 +++++----- .../public-data-access/index.mdx | 28 ++--- .../signed-in-user-data-access/index.mdx | 30 ++--- .../index.mdx | 4 +- .../user-group-based-data-access/index.mdx | 64 +++++------ .../index.mdx | 30 ++--- .../data/data-modeling/add-fields/index.mdx | 6 +- .../data/mutate-data/index.mdx | 32 +++--- .../build-a-backend/data/query-data/index.mdx | 35 +++--- .../data/set-up-data/index.mdx | 106 ++++++++++-------- .../data/subscribe-data/index.mdx | 66 ++++++----- 18 files changed, 357 insertions(+), 330 deletions(-) diff --git a/src/pages/gen2/build-a-backend/data/connect-from-server-runtime/index.mdx b/src/pages/gen2/build-a-backend/data/connect-from-server-runtime/index.mdx index ad754d52ea1..58fd3b54006 100644 --- a/src/pages/gen2/build-a-backend/data/connect-from-server-runtime/index.mdx +++ b/src/pages/gen2/build-a-backend/data/connect-from-server-runtime/index.mdx @@ -76,7 +76,7 @@ import { cookies } from 'next/headers'; export const cookieBasedClient = generateServerClientUsingCookies({ config: amplifyConfig, - cookies + cookies, }); ``` @@ -99,11 +99,11 @@ import { generateServerClientUsingReqRes } from '@aws-amplify/adapter-nextjs/dat import amplifyConfig from '@/amplifyconfiguration.json'; export const { runWithAmplifyServerContext } = createServerRunner({ - config: amplifyConfig + config: amplifyConfig, }); export const reqResBasedClient = generateServerClientUsingReqRes({ - config: amplifyConfig + config: amplifyConfig, }); ``` @@ -128,23 +128,23 @@ You can make any available query or mutation request with the generated server D Import the cookie-based server Data client in your Next.js React Server Component code and make your API requests. ```ts -import { type Schema } from '@/amplify/data/resource' +import { type Schema } from '@/amplify/data/resource'; import { generateServerClientUsingCookies } from '@aws-amplify/adapter-nextjs/data'; -import amplifyConfig from '@/amplifyconfiguration.json' -import { cookies } from 'next/headers' +import amplifyConfig from '@/amplifyconfiguration.json'; +import { cookies } from 'next/headers'; export const cookieBasedClient = generateServerClientUsingCookies({ config: amplifyConfig, - cookies -}) + cookies, +}); -async fetchTodos() { - const { data: todos, errors } = await cookieBasedClient.models.Todo.list() +const fetchTodos = async () => { + const { data: todos, errors } = await cookieBasedClient.models.Todo.list(); if (!errors) { - return todos + return todos; } -} +}; ``` @@ -156,27 +156,34 @@ Import the NextRequest/NextResponse-based server Data client in your Next.js ser For example, in a Next.js Pages Router API route, use the `req` and `res` parameters from the `handler` function with `runWithAmplifyServerContext`: ```ts -import { type Schema } from '@/amplify/data/resource' -import type { NextApiRequest, NextApiResponse } from 'next' -import { runWithAmplifyServerContext, reqResBasedClient } from '@/utils/amplifyServerUtils' +import { type Schema } from '@/amplify/data/resource'; +import type { NextApiRequest, NextApiResponse } from 'next'; +import { + runWithAmplifyServerContext, + reqResBasedClient, +} from '@/utils/amplifyServerUtils'; type ResponseData = { - todos: Schema["Todo"][] -} + todos: Schema['Todo'][]; +}; export default async function handler( - req: NextApiRequest, - res: NextApiResponse + request: NextApiRequest, + response: NextApiResponse ) { const todos = await runWithAmplifyServerContext({ - nextServerContext: { req, res} + nextServerContext: { request, response }, operation: async (contextSpec) => { - const { data: todos } = await reqResBasedClient.models.Todo.list(contextSpec) - return todos - } - }) - res.status(200).json({ todos }) + const { data: todos } = await reqResBasedClient.models.Todo.list( + contextSpec + ); + return todos; + }, + }); + + response.status(200).json({ todos }); } + ``` diff --git a/src/pages/gen2/build-a-backend/data/connect-to-API/index.mdx b/src/pages/gen2/build-a-backend/data/connect-to-API/index.mdx index 43a07b687e0..a0fa3824ab7 100644 --- a/src/pages/gen2/build-a-backend/data/connect-to-API/index.mdx +++ b/src/pages/gen2/build-a-backend/data/connect-to-API/index.mdx @@ -36,7 +36,7 @@ When you deploy you're iterating on your backend (`npx amplify sandbox`), an `am ```ts import { Amplify } from 'aws-amplify'; -import config from './amplifyconfiguration.json'; +import config from '../amplifyconfiguration.json'; Amplify.configure(config); ``` @@ -47,15 +47,15 @@ Once the Amplify library is configured, you can generate a "Data client" for you ```ts import { generateClient } from 'aws-amplify/data'; -import type { Schema } from '@/amplify/data/resource'; // Path to your backend resource definition +import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition const client = generateClient(); // Now you should be able to make CRUDL operations with the // Data client -async function fetchTodos() { +const fetchTodos = async () => { const { data: todos, errors } = await client.models.Todo.list(); -} +}; ``` ## Configure authorization mode @@ -74,10 +74,10 @@ To apply the same authorization mode on all requests from a Data client, specify ```ts import { generateClient } from 'aws-amplify/data'; -import type { Schema } from '@/amplify/data/resource'; // Path to your backend resource definition +import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition const client = generateClient({ - authMode: 'apiKey' + authMode: 'apiKey', }); ``` @@ -86,10 +86,10 @@ const client = generateClient({ ```ts import { generateClient } from 'aws-amplify/data'; -import type { Schema } from '@/amplify/data/resource'; // Path to your backend resource definition +import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition const client = generateClient({ - authMode: 'userPool' + authMode: 'userPool', }); ``` @@ -98,10 +98,10 @@ const client = generateClient({ ```ts import { generateClient } from 'aws-amplify/data'; -import type { Schema } from '@/amplify/data/resource'; // Path to your backend resource definition +import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition const client = generateClient({ - authMode: 'iam' + authMode: 'iam', }); ``` @@ -110,10 +110,10 @@ const client = generateClient({ ```ts import { generateClient } from 'aws-amplify/data'; -import type { Schema } from '@/amplify/data/resource'; // Path to your backend resource definition +import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition const client = generateClient({ - authMode: 'oidc' + authMode: 'oidc', }); ``` @@ -124,14 +124,14 @@ You can implement your own custom API authorization logic using a AWS Lambda fun ```ts import { generateClient } from 'aws-amplify/data'; -import type { Schema } from '@/amplify/data/resource'; // Path to your backend resource definition +import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const client = generateClient({ authMode: 'lambda', - authToken: lambdaAuthToken + authToken: lambdaAuthToken, }); ``` @@ -148,7 +148,7 @@ You can also specify the authorization mode on each individual API request. This ```ts const { data: todos, errors } = await client.models.Todo.list({ - authMode: 'apiKey' + authMode: 'apiKey', }); ``` @@ -157,7 +157,7 @@ const { data: todos, errors } = await client.models.Todo.list({ ```ts const { data: todos, errors } = await client.models.Todo.list({ - authMode: 'userPool' + authMode: 'userPool', }); ``` @@ -166,7 +166,7 @@ const { data: todos, errors } = await client.models.Todo.list({ ```ts const { data: todos, errors } = await client.models.Todo.list({ - authMode: 'iam' + authMode: 'iam', }); ``` @@ -175,7 +175,7 @@ const { data: todos, errors } = await client.models.Todo.list({ ```ts const { data: todos, errors } = await client.models.Todo.list({ - authMode: 'oidc' + authMode: 'oidc', }); ``` @@ -189,9 +189,9 @@ const getAuthToken = () => 'myAuthToken'; const lambdaAuthToken = getAuthToken(); const { data: todos, errors } = await client.models.Todo.list({ - authMode: 'lambda' - authToken: lambdaAuthToken -}) + authMode: 'lambda', + authToken: lambdaAuthToken, +}); ``` @@ -206,13 +206,13 @@ When working with the Amplify Data endpoint, you may need to set request headers ```ts -import type { Schema } from '@/amplify/schema'; +import type { Schema } from '../amplify/data/resource'; import { generateClient } from 'aws-amplify/data'; const client = generateClient({ headers: { - 'My-Custom-Header': 'my value' - } + 'My-Custom-Header': 'my value', + }, }); ``` @@ -222,11 +222,14 @@ const client = generateClient({ ```ts // same way for all CRUDL: .create, .get, .update, .delete, .list, .observeQuery -const res = await client.models.Blog.get('myBlogId', { - headers: { - 'My-Custom-Header': 'my value' +const { data: blog, errors } = await client.models.Blog.get( + { id: 'myBlogId' }, + { + headers: { + 'My-Custom-Header': 'my value', + }, } -}); +); ``` @@ -240,7 +243,7 @@ The examples above show you how to set static headers but you can also programma ```ts -import type { Schema } from '@/amplify/schema'; +import type { Schema } from '../amplify/data/resource'; import { generateClient } from 'aws-amplify/data'; const client = generateClient({ @@ -257,9 +260,9 @@ const client = generateClient({ } */ return { - 'My-Custom-Header': 'my value' + 'My-Custom-Header': 'my value', }; - } + }, }); ``` @@ -269,10 +272,12 @@ const client = generateClient({ ```ts // same way for all CRUDL: .create, .get, .update, .delete, .list, .observeQuery -const res = await client.models.Blog.get('myBlogId', { - headers: async (requestOptions) => { - console.log(requestOptions); - /* The request options allow you to customize your headers based on the request options such +const res = await client.models.Blog.get( + { id: 'myBlogId' }, + { + headers: async (requestOptions) => { + console.log(requestOptions); + /* The request options allow you to customize your headers based on the request options such as http method, headers, request URI, and query string. These options are typically used to create a request signature. { @@ -282,11 +287,12 @@ const res = await client.models.Blog.get('myBlogId', { queryString: "" } */ - return { - 'My-Custom-Header': 'my value' - }; + return { + 'My-Custom-Header': 'my value', + }; + }, } -}); +); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/configure-custom-identity-and-group-claim/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/configure-custom-identity-and-group-claim/index.mdx index 97b02521070..7d412062548 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/configure-custom-identity-and-group-claim/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/configure-custom-identity-and-group-claim/index.mdx @@ -16,7 +16,7 @@ Amplify Data supports using custom identity and group claims if you do not wish To use custom claims specify `identityClaim` or `groupClaim` as appropriate. In the example below, the `identityClaim` is specified and the record owner will check against this `user_id` claim. Similarly, if the `user_groups` claim contains a "Moderator" string then access will be granted. ```ts -import { a, defineData, type ClientSchema } from "@aws-amplify/backend"; +import { a, defineData, type ClientSchema } from '@aws-amplify/backend'; const schema = a.schema({ Post: a @@ -27,12 +27,13 @@ const schema = a.schema({ content: a.string(), }) .authorization([ - a.allow.owner().identityClaim("user_id"), - a.allow.specificGroups(["Moderator"]).withClaimIn("user_groups"), + a.allow.owner().identityClaim('user_id'), + a.allow.specificGroups(['Moderator']).withClaimIn('user_groups'), ]), }); export type Schema = ClientSchema; export const data = defineData({ schema }); + ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/custom-data-access-patterns/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/custom-data-access-patterns/index.mdx index 2177b38171f..0fa50967900 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/custom-data-access-patterns/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/custom-data-access-patterns/index.mdx @@ -18,17 +18,17 @@ import { type ClientSchema, a, defineData, - defineFunction + defineFunction, } from '@aws-amplify/backend'; const schema = a.schema({ Todo: a .model({ - content: a.string() + content: a.string(), }) // STEP 1 // Indicate which models / fields should use a custom authorization rule - .authorization([a.allow.custom()]) + .authorization([a.allow.custom()]), }); export type Schema = ClientSchema; @@ -41,13 +41,13 @@ export const data = defineData({ // Pass in the function to be used for a custom authorization rule lambdaAuthorizationMode: { function: defineFunction({ - entry: './custom-authorizer.ts' + entry: './custom-authorizer.ts', }), // (Optional) STEP 3 // Configure the token's time to live - timeToLiveInSeconds: 300 - } - } + timeToLiveInSeconds: 300, + }, + }, }); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx index cd206f362a0..a18903bcc53 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx @@ -17,13 +17,15 @@ To grant an external AWS resource or an IAM role access to the Amplify Data API, ```ts title="amplify/data/resource.ts" // amplify/data/resource.ts -import { a, defineData, type ClientSchema } from "@aws-amplify/backend"; +import { a, defineData, type ClientSchema } from '@aws-amplify/backend'; const schema = a.schema({ - Todo: a.model({ - name: a.string(), - description: a.string(), - }), + Todo: a + .model({ + name: a.string(), + description: a.string(), + }) + .authorization([a.allow.public()]), }); export type Schema = ClientSchema; @@ -31,9 +33,11 @@ export type Schema = ClientSchema; export const data = defineData({ schema, authorizationModes: { + defaultAuthorizationMode: 'apiKey', + // Pass in the Lambda Execution Role names to grant full read/write access to the API // if their IAM policies permit it. - allowListedRoleNames: ["lambdaRole"], + allowListedRoleNames: ['lambdaRole'], }, }); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx index 2aaa4ecddf4..c7dc5f81010 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/index.mdx @@ -20,12 +20,12 @@ export function getStaticProps(context) { Use the `.authorization()` modifier to configure authorization rules for public, signed-in user, per user, and per user group data access. **Authorization rules operate on the deny-by-default principle**. Meaning that if an authorization rule is not specifically configured, it is denied. ```ts -export const schema = a.schema({ +const schema = a.schema({ Post: a.model({ content: a.string() }).authorization([ // Allow anyone auth'd with an API key to read everyone's posts. - a.allow.public().to(['read']), + a.allow.public().to(['read']), // Allow signed-in user to create, read, update, // and delete their __OWN__ posts. a.allow.owner(), @@ -55,7 +55,7 @@ Amplify will always use the most specific authorization rule that is available. If there are multiple authorization rules present, they will be logically OR'ed. Review [Configure multiple authorization rules](#configure-multiple-authorization-rules) to learn more. -Finally, there are special "Admin IAM Roles" that allow you to overrule any authorization logic applied on the API and determine the API access completely via its IAM policy. +Finally, there are special "Admin IAM Roles" that allow you to overrule any authorization logic applied on the API and determine the API access completely via its IAM policy. ### Global authorization rule (only for getting started) @@ -64,7 +64,7 @@ To help you get started, you can define an authorization rule on the data schema The global authorization rule below uses `a.allow.public()`. This example allows anyone to create, read, update, and delete and is applied to every data model. ```ts -export const schema = a.schema({ +const schema = a.schema({ // Because no model-level authorization rule is present // this model will use the global authorization rule. Todo: a.model({ @@ -85,10 +85,10 @@ export const schema = a.schema({ ### Model-level authorization rules -Add an authorization rule to a model to apply the authorization rule to all fields of that model. +Add an authorization rule to a model to apply the authorization rule to all fields of that model. ```ts -export const schema = a.schema({ +const schema = a.schema({ Post: a.model({ content: a.string(), createdBy: a.string() @@ -96,7 +96,7 @@ export const schema = a.schema({ // All fields (content, createdBy) will be protected by // this authorization rule }).authorization([ - a.allow.public().to(['read']), + a.allow.public().to(['read']), a.allow.owner(), ]) }) @@ -112,7 +112,7 @@ In the example below: - Only the `ssn` field has `owner` auth applied and this field-level auth rule means that model-level auth rules are not applied ```ts -export const schema = a.schema({ +const schema = a.schema({ Employee: a.model({ name: a.string(), email: a.string(), @@ -137,7 +137,7 @@ When combining multiple authorization rules, they are "logically OR"-ed. In the - Owners are allowed to create, read, update, and delete their own posts ```ts -export const schema = a.schema({ +const schema = a.schema({ Post: a.model({ title: a.string(), content: a.string() diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx index 1517026a56b..5124f3d60b8 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/multi-user-data-access/index.mdx @@ -19,13 +19,13 @@ The `multipleOwners` rule grants a set of users access to a record by automatica If you want to grant a set of users access to a record, you use the `multipleOwners` rule. This automatically creates a `owner: a.string().array()` field to store the allowed owners. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string(), - }).authorization([ - a.allow.multipleOwners(), - ]), -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.multipleOwners()]), +}); ``` ## Override to a list of owners @@ -33,12 +33,12 @@ export const schema = a.schema({ You can override the `inField` to a list of owners. Use this if you want a dynamic set of users to have access to a record. In the example below, the `authors` list is populated with the creator of the record upon record creation. The creator can then update the `authors` field with additional users. Any user listed in the `authors` field can access the record. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string(), - authors: a.string().array(), // record owner information now stored in "authors" field - }).authorization([ - a.allow.multipleOwners().inField("authors"), - ]), -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + authors: a.string().array(), // record owner information now stored in "authors" field + }) + .authorization([a.allow.multipleOwners().inField('authors')]), +}); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/per-user-per-owner-data-access/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/per-user-per-owner-data-access/index.mdx index 3dad0544f07..857c65cc002 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/per-user-per-owner-data-access/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/per-user-per-owner-data-access/index.mdx @@ -20,25 +20,25 @@ You can use the `owner` authorization strategy to restrict a record's access to ```ts // The "owner" of a Todo is allowed to create, read, update, and delete their own todos -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - }).authorization([ - a.allow.owner() - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.owner()]), +}); ``` ```ts // The "owner" of a Todo record is only allowed to create, read, and update it. // The "owner" of a Todo record is denied to delete it. -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - }).authorization([ - a.allow.owner().to(["create", "read", "update"]) - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.owner().to(['create', 'read', 'update'])]), +}); ``` Behind the scenes, Amplify will automatically add a `owner: a.string()` field to each record which contains the record owner's identity information upon record creation. @@ -52,14 +52,14 @@ By default, the Cognito user pool's user information is populated into the `owne To prevent an owner from reassigning their record to another user, protect the owner field (by default `owner: String`) with a [field-level authorization rule](/gen2/build-a-backend/data/customize-authz/#field-level-authorization-rules). For example, in a social media app, you would want to prevent Alice from being able to reassign Alice's Post to Bob. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - owner: a.string().authorization([a.allow.owner().to(["read", "delete"])]) - }).authorization([ - a.allow.owner() - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + owner: a.string().authorization([a.allow.owner().to(['read', 'delete'])]), + }) + .authorization([a.allow.owner()]), +}); ```
@@ -69,12 +69,12 @@ export const schema = a.schema({ You can override the `owner` field to your own preferred field, by specifying a custom `ownerField` in the authorization rule. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - author: a.string() // record owner information now stored in "author" field - }).authorization([ - a.allow.owner().inField("author") - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + author: a.string(), // record owner information now stored in "author" field + }) + .authorization([a.allow.owner().inField('author')]), +}); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/public-data-access/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/public-data-access/index.mdx index 9153c14aedd..3ad4d8d7ed3 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/public-data-access/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/public-data-access/index.mdx @@ -18,13 +18,13 @@ The public authorization strategy grants everyone access to the API, which is pr To grant everyone access, use the `.public()` authorization strategy. Behind the scenes, the API will be protected with an API key. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - }).authorization([ - a.allow.public() - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.public()]), +}); ``` ## Add public authorization rule using IAM authentication @@ -32,11 +32,11 @@ export const schema = a.schema({ You can also override the authorization provider. In the example below, `iam` is specified as the provider which allows you to use an "Unauthenticated Role" from the Cognito identity pool for public access instead of an API key. Your Auth resources defined in `amplify/auth/resource.ts` generates scoped down IAM policies for the "Unauthenticated role" in the Cognito identity pool automatically. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - }).authorization([ - a.allow.public("iam") - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.public('iam')]), +}); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/signed-in-user-data-access/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/signed-in-user-data-access/index.mdx index d052614d4be..cab9607992b 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/signed-in-user-data-access/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/signed-in-user-data-access/index.mdx @@ -15,7 +15,7 @@ The `private` authorization strategy restricts record access to only signed-in u ## Add signed-in user authorization rule -You can use the `private` authorization strategy to restrict a record's access to every signed-in user. +You can use the `private` authorization strategy to restrict a record's access to every signed-in user. **Note:** If you want to restrict a record's access to a specific user, see [Per-user/per-owner data access](/gen2/build-a-backend/data/customize-authz/per-user-per-owner-data-access/). The `private` authorization strategy detailed on this page applies the authorization rule for data access to **every** signed-in user. @@ -24,13 +24,13 @@ You can use the `private` authorization strategy to restrict a record's access t In the example below, anyone with a valid JWT token from the Cognito user pool is allowed access to all Todos. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - }).authorization([ - a.allow.private() - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.private()]), +}); ``` ## Override the authentication provider @@ -38,13 +38,13 @@ export const schema = a.schema({ You can also override the authorization provider. In the example below, `iam` is specified as the provider which allows you to use an "Unauthenticated Role" from the Cognito identity pool for public access instead of an API key. Your Auth resources defined in `amplify/auth/resource.ts` generates scoped down IAM policies for the "Unauthenticated role" in the Cognito identity pool automatically. ```ts -export const schema = a.schema({ - Todo: a.model({ - content: a.string() - }).authorization([ - a.allow.private("iam") - ]) -}) +const schema = a.schema({ + Todo: a + .model({ + content: a.string(), + }) + .authorization([a.allow.private('iam')]), +}); ``` In addition, you can also use OpenID Connect with `private` authorization. See [OpenID Connect as an authorization provider](/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider/). diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/use-iam-authz-within-appsync-console/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/use-iam-authz-within-appsync-console/index.mdx index 8edb66deb61..38dd1d53403 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/use-iam-authz-within-appsync-console/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/use-iam-authz-within-appsync-console/index.mdx @@ -17,7 +17,7 @@ To grant an external AWS Resource or an IAM role access to Amplify Data's API, y ```ts title="amplify/data/resource.ts" // amplify/data/resource.ts -import { a, defineData, type ClientSchema } from "@aws-amplify/backend"; +import { a, defineData, type ClientSchema } from '@aws-amplify/backend'; const schema = a.schema({ Todo: a.model({ @@ -33,7 +33,7 @@ export const data = defineData({ authorizationModes: { // Pass in the IAM user's role name to grant full read/write access to the API // if their IAM policies permit it. - allowListedRoleNames: ["userRole"], + allowListedRoleNames: ['userRole'], }, }); ``` diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/user-group-based-data-access/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/user-group-based-data-access/index.mdx index 2fe083ee1a0..1f427cbe2c5 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/user-group-based-data-access/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/user-group-based-data-access/index.mdx @@ -19,28 +19,28 @@ When you want to restrict access to a specific set of user groups, provide the g ```ts // allow one specific group -export const schema = a.schema({ - Salary: a.model({ - wage: a.float(), - currency: a.string() - }).authorization([ - a.allow.specificGroup("Admin") - ]) -}) +const schema = a.schema({ + Salary: a + .model({ + wage: a.float(), + currency: a.string(), + }) + .authorization([a.allow.specificGroup('Admin')]), +}); ``` This can then be updated to allow access to multiple defined groups; in this example below we added access for "Leadership". ```ts // allow multiple specific groups -export const schema = a.schema({ - Salary: a.model({ - wage: a.float(), - currency: a.string() - }).authorization([ - a.allow.specificGroups(["Admin", "Leadership"]) - ]) -}) +const schema = a.schema({ + Salary: a + .model({ + wage: a.float(), + currency: a.string(), + }) + .authorization([a.allow.specificGroups(['Admin', 'Leadership'])]), +}); ``` ## Add authorization rules for dynamically set user groups @@ -49,26 +49,26 @@ With dynamic group authorization, each record contains an attribute specifying w ```ts // Dynamic group authorization with multiple groups -a.schema({ - Post: a.model({ - title: a.string(), - groups: a.string().array() - }).authorization([ - a.allow.groupsDefinedInField('groups'), - ]) -}) +const schema = a.schema({ + Post: a + .model({ + title: a.string(), + groups: a.string().array(), + }) + .authorization([a.allow.groupsDefinedIn('groups')]), +}); ``` ```ts // Dynamic group authorization with a single group -a.schema({ - Post: a.model({ - title: a.string(), - groups: a.string() - }).authorization([ - a.allow.groupDefinedIn('groups'), - ]) -}) +const schema = a.schema({ + Post: a + .model({ + title: a.string(), + groups: a.string(), + }) + .authorization([a.allow.groupDefinedIn('groups')]), +}); ``` By default, `group` authorization leverages Amazon Cognito user pool groups but you can also use OpenID Connect with `group` authorization. See [OpenID Connect as an authorization provider](/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider). diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider/index.mdx index 54048924cff..e1f3fecb21d 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider/index.mdx @@ -17,19 +17,20 @@ The example below highlights the supported authorization strategies with a `oidc ```ts title="amplify/data/resource.ts" // amplify/data/resource.ts -import { a, defineData, type ClientSchema } from "@aws-amplify/backend"; +import { a, defineData, type ClientSchema } from '@aws-amplify/backend'; const schema = a.schema({ - Todo: a.model({ - content: a.string(), - }) - .authorization([ - a.allow.owner("oidc").identityClaim("user_id"), - a.allow.private("oidc"), - a.allow - .specificGroups(["testGroupName"], "oidc") - .withClaimIn("user_groups"), - ]), + Todo: a + .model({ + content: a.string(), + }) + .authorization([ + a.allow.owner('oidc').identityClaim('user_id'), + a.allow.private('oidc'), + a.allow + .specificGroups(['testGroupName'], 'oidc') + .withClaimIn('user_groups'), + ]), }); export type Schema = ClientSchema; @@ -37,10 +38,11 @@ export type Schema = ClientSchema; export const data = defineData({ schema, authorizationModes: { + defaultAuthorizationMode: 'oidc', oidcAuthorizationMode: { - oidcProviderName: "oidc-provider-name", - oidcIssuerUrl: "https://example.com", - clientId: "client-id", + oidcProviderName: 'oidc-provider-name', + oidcIssuerUrl: 'https://example.com', + clientId: 'client-id', tokenExpiryFromAuthInSeconds: 300, tokenExpireFromIssueInSeconds: 600, }, diff --git a/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx b/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx index a004782e1c2..c8a6e391912 100644 --- a/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx +++ b/src/pages/gen2/build-a-backend/data/data-modeling/add-fields/index.mdx @@ -140,7 +140,7 @@ const availableSettings = client.enums.PrivacySetting.values() By default, fields are optional. To mark a field as required, use the `.required()` modifier. ```ts -export const schema = a.schema({ +const schema = a.schema({ Todo: a.model({ content: a.string().required() }) @@ -152,7 +152,7 @@ export const schema = a.schema({ Any field can be modified to be an array using the `.array()` modifier. ```ts -export const schema = a.schema({ +const schema = a.schema({ Todo: a.model({ content: a.string().required(), notes: a.string().array() @@ -165,7 +165,7 @@ export const schema = a.schema({ You can use the `.default(...)` modifier to specify a default value for optional [scalar type fields and arrays](https://docs.aws.amazon.com/appsync/latest/devguide/scalars.html). The `.default(...)` modifier is not available for custom types, enums, or relationships. ```ts -export const schema = a.schema({ +const schema = a.schema({ Todo: a.model({ content: a.string().default("My new Todo") }) diff --git a/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx b/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx index 344e10312f3..e72423b880b 100644 --- a/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/mutate-data/index.mdx @@ -24,13 +24,13 @@ You can create an item by first generating the Data client with your backend Dat ```js import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource' +import { type Schema } from '../amplify/data/resource' const client = generateClient(); const { errors, data: newTodo } = await client.models.Todo.create({ content: "My new todo", - done: true, + isDone: true, }) ``` @@ -46,16 +46,16 @@ To update the item, use the `update` function: ```js import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource' +import { type Schema } from '../amplify/data/resource'; const client = generateClient(); const todo = { id: 'some_id', - description: 'Updated description' + content: 'Updated content', }; -const { data: updatedTodo, errors } = await client.models.Todo.update(todo) +const { data: updatedTodo, errors } = await client.models.Todo.update(todo); ``` @@ -73,12 +73,12 @@ You can then delete the Todo by using the delete mutation. To specify which item ```js import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource' +import { type Schema } from '../amplify/data/resource' const client = generateClient(); const toBeDeletedTodo = { - id: "123123213" + id: '123123213' } const { data: deletedTodo, errors } = await client.models.Todo.delete(toBeDeletedTodo) @@ -96,17 +96,17 @@ Each API request uses an authorization mode. If you get unauthorized errors, you ```ts import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource'; +import { type Schema } from '../amplify/data/resource'; const client = generateClient(); const { errors, data: newTodo } = await client.models.Todo.create( { content: 'My new todo', - done: true + isDone: true, }, { - authMode: 'apiKey' + authMode: 'apiKey', } ); ``` @@ -117,9 +117,9 @@ const { errors, data: newTodo } = await client.models.Todo.create( You can cancel any mutation API request by calling `.cancel` on the mutation request promise that's returned by `.create(...)`, `.update(...)`, or `.delete(...)`. -```javascript -const promise = client.models.Todo.create({ content: "New Todo "}) - // ^ Note: we're not awaiting the request, we're returning the promise +```ts +const promise = client.models.Todo.create({ content: 'New Todo ' }); +// ^ Note: we're not awaiting the request, we're returning the promise try { await promise; @@ -132,15 +132,15 @@ try { } } -... +//... // To cancel the above request -client.cancel(promise, "my message for cancellation"); +client.cancel(promise, 'my message for cancellation'); ``` You need to ensure that the promise returned from `.create()`, `.update()`, and `.delete()` has not been modified. Typically, async functions wrap the promise being returned into another promise. For example, the following will **not** work: -```javascript +```ts async function makeAPICall() { return client.models.Todo.create({ content: 'New Todo' }); } diff --git a/src/pages/gen2/build-a-backend/data/query-data/index.mdx b/src/pages/gen2/build-a-backend/data/query-data/index.mdx index 94f6c84f1d0..d9add0ccfb5 100644 --- a/src/pages/gen2/build-a-backend/data/query-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/query-data/index.mdx @@ -41,9 +41,8 @@ const client = generateClient(); const { data: todos, errors } = await client.models.Todo.list(); // get a specific item -const { data: inventory, errors } = await client.models.Inventory.get({ - productId: '...', - warehouseId: '...' +const { data: todo, errors } = await client.models.Todo.get({ + id: '...', }); ``` @@ -187,13 +186,9 @@ A **custom selection set** allows consumers to specify, on a per-call basis, the ```ts // same way for all CRUDL: .create, .get, .update, .delete, .list, .observeQuery const { data: blogWithSubsetOfData, errors } = await client.models.Blog.get( - '', + { id: blog.id }, { - selectionSet: [ - 'author.email', - 'publication.company.location.city', - 'content.*' - ] + selectionSet: ['author.email', 'posts.*'], } ); ``` @@ -213,19 +208,20 @@ const [posts, setPosts] = useState([]); You can combine the `Schema["MODEL_NAME"]` type with the `SelectionSet` helper type to describe the return type of API requests using the `selectionSet` parameter: ```ts -import { type SelectionSet } from 'aws-amplify/data' -import { type Schema } from '@/amplify/data/resource' - +import type { SelectionSet } from 'aws-amplify/data' +import type { Schema } from '../amplify/data/resource'; -const selectionSet = ['content', 'author', 'comments.*'] as const; -type PostWithComments = SelectionSet; +const selectionSet = ['content', 'blog.author.*', 'comments.*'] as const; +type PostWithComments = SelectionSet; // ... const [posts, setPosts] = useState([]); -async fetchPosts() { - const { data: postsWithComments } = await client.models.Post.list({ selectionSet }) - setPosts(postsWithComments) +const fetchPosts = async () => { + const { data: postsWithComments } = await client.models.Post.list({ + selectionSet, + }); + setPosts(postsWithComments); } ``` @@ -234,8 +230,8 @@ async fetchPosts() { You can cancel any query API request by calling `.cancel` on the query request promise that's returned by `.list(...)` or `.get(...)`. ```javascript -const promise = client.models.Todo.list() - // ^ Note: we're not awaiting the request, we're returning the promise +const promise = client.models.Todo.list(); +// ^ Note: we're not awaiting the request, we're returning the promise try { await promise; @@ -247,7 +243,6 @@ try { // handle user cancellation logic } } - ... // To cancel the above request diff --git a/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx b/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx index f3e9e32e016..f54724275b1 100644 --- a/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/set-up-data/index.mdx @@ -45,7 +45,13 @@ const schema = a.schema({ export type Schema = ClientSchema; // defines the data resource to be deployed -export const data = defineData({ schema }); +export const data = defineData({ + schema, + authorizationModes: { + defaultAuthorizationMode: 'apiKey', + apiKeyAuthorizationMode: { expiresInDays: 30 } + } +}); ``` Every `a.model()` automatically creates the following resources in the cloud: @@ -78,7 +84,7 @@ In your app's entry point, typically **main.tsx** for React apps created using V // main.tsx import { Amplify } from 'aws-amplify'; -import config from '@/amplifyconfiguration.json'; +import config from '../amplifyconfiguration.json'; Amplify.configure(config); ``` @@ -90,15 +96,13 @@ Let's first add a button to create a new todo item. To make a "create Todo" API ```tsx title="TodoList.tsx" // TodoList.tsx -"use client" -import type { Schema } from '@/amplify/data/resource' +import type { Schema } from '../amplify/data/resource' import { generateClient } from 'aws-amplify/data' -import { client } from '@/utils/data' const client = generateClient() export default function TodoList() { - async function createTodo() => { + const createTodo = async () => { await client.models.Todo.create({ content: window.prompt("Todo content?"), isDone: false @@ -126,40 +130,43 @@ Next, list all your todos and then refetch the todos after a todo has been added ```tsx title="TodoList.tsx" // TodoList.tsx -"use client" -import type { Schema } from '@/amplify/data/resource' -import { useState, useEffect } from 'react' -import { generateClient } from 'aws-amplify/data' +import { useState, useEffect } from "react"; +import type { Schema } from "../amplify/data/resource"; +import { generateClient } from "aws-amplify/data"; -const client = generateClient() +const client = generateClient(); export default function TodoList() { - const [todos, setTodos] = useState([]) + const [todos, setTodos] = useState([]); const fetchTodos = async () => { - const { data: items, errors } = await client.models.Todo.list() - setTodos(items) - } + const { data: items, errors } = await client.models.Todo.list(); + setTodos(items); + }; - useEffect(() => { fetchTodos() }, []) + useEffect(() => { + fetchTodos(); + }, []); - async function createTodo() => { + const createTodo = async () => { await client.models.Todo.create({ content: window.prompt("Todo content?"), - isDone: false - }) + isDone: false, + }); - fetchTodos() + fetchTodos(); } return (
    - {todos.map({ id, content } =>
  • {content}
  • )} + {todos.map(({ id, content }) => ( +
  • {content}
  • + ))}
- ) + ); } ``` @@ -170,43 +177,44 @@ You can also use `observeQuery` to subscribe to a live feed of your backend data ```tsx title="App.tsx" // App.tsx -"use client" -import type { Schema } from '@/amplify/data/resource' -import { useState, useEffect } from 'react' -import { generateClient } from 'aws-amplify/data' +import type { Schema } from "../amplify/data/resource"; +import { useState, useEffect } from "react"; +import { generateClient } from "aws-amplify/data"; -const client = generateClient() +const client = generateClient(); export default function TodoList() { - const [todos, setTodos] = useState([]) + const [todos, setTodos] = useState([]); useEffect(() => { - const sub = client.models.Todo - .observeQuery() - .subscribe({ - next: ({ items }) => { - setTodos([...items]) - } - }) - - return () => sub.unsubscribe() - }, []) - - async function createTodo() => { + const sub = client.models.Todo.observeQuery().subscribe({ + next: ({ items }) => { + setTodos([...items]); + }, + }); + + return () => sub.unsubscribe(); + }, []); + + const createTodo = async () => { await client.models.Todo.create({ content: window.prompt("Todo content?"), - isDone: false - }) + isDone: false, + }); // no more manual refetchTodos required! // - fetchTodos() - } + }; - return
- -
    - {todos.map({ id, content } =>
  • {content}
  • )} -
-
+ return ( +
+ +
    + {todos.map(({ id, content }) => ( +
  • {content}
  • + ))} +
+
+ ); } ``` diff --git a/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx b/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx index c9e61444339..aeedde0bc86 100644 --- a/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx +++ b/src/pages/gen2/build-a-backend/data/subscribe-data/index.mdx @@ -24,30 +24,33 @@ Before you begin, you will need: The recommended way to fetch a list of data is to use `observeQuery` to get a real-time list of your app data at all times. You can integrate `observeQuery` with React's `useState` and `useEffect` hooks in the following way: ```ts -import { useState, useEffect } from 'react' +import { useState, useEffect } from 'react'; import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource' +import type { Schema } from '../amplify/data/resource'; -type Todo = Schema["Todo"] +type Todo = Schema['Todo']; const client = generateClient(); -function MyComponent() { - const [todos, setTodos] = useState([]) +export default function MyComponent() { + const [todos, setTodos] = useState([]); useEffect(() => { - const sub = client.models.Todo.observeQuery() - .subscribe({ - next: ({ items, isSynced }) => { - setTodos([...items]) - } - }) - return () => sub.unsubscribe() - }, []) - - return
    - {todos.map(todo =>
  • {todo.content}
  • )} -
+ const sub = client.models.Todo.observeQuery().subscribe({ + next: ({ items, isSynced }) => { + setTodos([...items]); + }, + }); + return () => sub.unsubscribe(); + }, []); + + return ( +
    + {todos.map((todo) => ( +
  • {todo.content}
  • + ))} +
+ ); } ``` @@ -59,26 +62,26 @@ Subscriptions is a feature that allows the server to send data to its clients wh ```ts import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource'; +import type { Schema } from '../amplify/data/resource'; const client = generateClient(); // Subscribe to creation of Todo const createSub = client.models.Todo.onCreate().subscribe({ - next: ({ data }) => console.log(data), - error: (error) => console.warn(error) + next: (data) => console.log(data), + error: (error) => console.warn(error), }); // Subscribe to update of Todo const updateSub = client.models.Todo.onUpdate().subscribe({ - next: ({ data }) => console.log(data), - error: (error) => console.warn(error) + next: (data) => console.log(data), + error: (error) => console.warn(error), }); // Subscribe to deletion of Todo const deleteSub = client.models.Todo.onDelete().subscribe({ - next: ({ data }) => console.log(data), - error: (error) => console.warn(error) + next: (data) => console.log(data), + error: (error) => console.warn(error), }); // Stop receiving data updates from the subscription @@ -93,19 +96,19 @@ Subscriptions take an optional `filter` argument to define service-side subscrip ```ts import { generateClient } from 'aws-amplify/data'; -import { type Schema } from '@/amplify/data/resource'; +import type { Schema } from '../amplify/data/resource'; const client = generateClient(); const sub = client.models.Todo.onCreate({ filter: { content: { - contains: 'groceries' - } - } + contains: 'groceries', + }, + }, }).subscribe({ - next: ({ data }) => console.log(data), - error: (error) => console.warn(error) + next: (data) => console.log(data), + error: (error) => console.warn(error), }); ``` @@ -160,8 +163,9 @@ While offline, your application will miss messages and will not automatically ca ```js import { generateClient, CONNECTION_STATE_CHANGE, ConnectionState } from 'aws-amplify/data' import { Hub } from 'aws-amplify/utils' +import { Schema } from '../amplify/data/resource'; -const client = generateClient() +const client = generateClient() const fetchRecentData = () => { const { data: allTodos } = await client.models.Todo.list(); From 886f984630c2e19a97a150a64dc72207ce34c813 Mon Sep 17 00:00:00 2001 From: Roshane Pascual Date: Fri, 8 Mar 2024 17:06:22 -0800 Subject: [PATCH 53/91] Update sandbox --dir-to-watch description (#7031) --- src/pages/gen2/reference/cli-commands/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/gen2/reference/cli-commands/index.mdx b/src/pages/gen2/reference/cli-commands/index.mdx index 11a350e2bfc..1cb03d5b292 100644 --- a/src/pages/gen2/reference/cli-commands/index.mdx +++ b/src/pages/gen2/reference/cli-commands/index.mdx @@ -33,7 +33,7 @@ Sandbox enables you to develop your backend alongside your frontend's developmen Options -- `--dir-to-watch` (_string_) - Directory to watch for file changes. All subdirectories and files will be included. +- `--dir-to-watch` (_string_) - Directory to watch for file changes. All subdirectories and files will be included. Defaults to the amplify directory. - `--exclude` (_string[]_) - An array of paths or glob patterns to ignore. Paths can be relative or absolute and can either be files or directories. - `--name` (_string_) - An optional name to distinguish between different sandbox environments. Default is the name in your package.json. - `--config-out-dir` (_string_) - A path to a directory where the client config file is written. If not provided, defaults to the working directory of the current process. From 0be1575a601bbf5a15ac913e5c3a4b706567135d Mon Sep 17 00:00:00 2001 From: Tim Nguyen <54393192+timngyn@users.noreply.github.com> Date: Mon, 11 Mar 2024 09:46:27 -0700 Subject: [PATCH 54/91] Update index.mdx (#7035) --- src/pages/[platform]/start/getting-started/auth/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/[platform]/start/getting-started/auth/index.mdx b/src/pages/[platform]/start/getting-started/auth/index.mdx index e17d7aead48..939c49eb2a1 100644 --- a/src/pages/[platform]/start/getting-started/auth/index.mdx +++ b/src/pages/[platform]/start/getting-started/auth/index.mdx @@ -668,8 +668,8 @@ import { } from '@aws-amplify/ui-react-native'; import { Amplify } from 'aws-amplify'; -import amplifyconfig from './src/amplifyconfiguration.json'; -Amplify.configure(awsExports); +import config from './src/amplifyconfiguration.json'; +Amplify.configure(config); // retrieves only the current value of 'user' from 'useAuthenticator' const userSelector = (context) => [context.user]; From 8e1c3e32faeb93530c1c8ece04232128e2338138 Mon Sep 17 00:00:00 2001 From: Tim Nguyen <54393192+timngyn@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:43:17 -0700 Subject: [PATCH 55/91] Fix link (#7028) --- src/fragments/lib/troubleshooting/common/upgrading.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fragments/lib/troubleshooting/common/upgrading.mdx b/src/fragments/lib/troubleshooting/common/upgrading.mdx index 6bb8d66c1f6..bbfb0eb54cf 100644 --- a/src/fragments/lib/troubleshooting/common/upgrading.mdx +++ b/src/fragments/lib/troubleshooting/common/upgrading.mdx @@ -1,4 +1,4 @@ -This migration guide will help you upgrade your Amplify JavaScript project from v5 to v6. In order to provide you with a cleaner experience, better typings, improved support for NextJS, and improved [tree-shaking](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) leading to [a much smaller bundle-size](https://aws.amazon.com/blogs/mobile/building-fast-next-js-apps-using-typescript-and-aws-amplify-javascript-v6), we made the following changes for v6: +This migration guide will help you upgrade your Amplify JavaScript project from v5 to v6. In order to provide you with a cleaner experience, better typings, improved support for NextJS, and improved [tree-shaking](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking) leading to [a much smaller bundle-size](https://aws.amazon.com/blogs/mobile/amplify-javascript-v6/), we made the following changes for v6: 1. We have transitioned to an approach where you only import the features you need from each of our categories. 2. Most API’s now use named params instead of positional params, allowing for cleaner and more consistent typing. From 57a1f7e0fdded8296d6e748f3405c7eaa8768dd4 Mon Sep 17 00:00:00 2001 From: Charles Shin Date: Mon, 11 Mar 2024 12:35:09 -0700 Subject: [PATCH 56/91] add docs to auth resource access pattern (#7032) --- src/directory/directory.mjs | 3 + .../auth/admin-actions/index.mdx | 1 - .../grant-access-to-auth-resources/index.mdx | 89 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/pages/gen2/build-a-backend/auth/grant-access-to-auth-resources/index.mdx diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 4d889e1d8c4..4a407a093a1 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -2006,6 +2006,9 @@ export const directory = { { path: 'src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx' }, + { + path: 'src/pages/gen2/build-a-backend/auth/grant-access-to-auth-resources/index.mdx' + }, { path: 'src/pages/gen2/build-a-backend/auth/override-cognito/index.mdx' } diff --git a/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx b/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx index b16ab83ad8e..1518730c989 100644 --- a/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx @@ -9,7 +9,6 @@ export function getStaticProps(context) { meta } }; - } We do not yet have out-of-the-box support for Admin queries in Amplify (Gen 2). You will have to a create a new [AWS Lambda](https://aws.amazon.com/lambda/) resource, and provide it with the necessary permissions to access the [Amazon Cognito Admin Actions](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_Operations.html) behind the scenes. diff --git a/src/pages/gen2/build-a-backend/auth/grant-access-to-auth-resources/index.mdx b/src/pages/gen2/build-a-backend/auth/grant-access-to-auth-resources/index.mdx new file mode 100644 index 00000000000..1e4551c2538 --- /dev/null +++ b/src/pages/gen2/build-a-backend/auth/grant-access-to-auth-resources/index.mdx @@ -0,0 +1,89 @@ +export const meta = { + title: 'Grant access to auth resources', + description: 'Learn how to grant a resource access to auth resources' +}; + +export function getStaticProps(context) { + return { + props: { + meta + } + }; +} + + + +**Under active development:** The Authentication experience for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose + + + +### Grant function access + +You can grant your backend function access to authentication resources, which will enable the function to perform administrative operations such as creating users, handling user password recovery, or adding users to groups. + +```ts title="amplify/function/my-demo-function/resource.ts" +import { defineFunction } from '@aws-amplify/backend'; + +export const demoFunction = defineFunction({}); +``` + +```ts title="amplify/auth/resource.ts" +import { defineAuth, defineFunction } from '@aws-amplify/backend'; +import { demoFunction } from '../function/my-demo-function/resource'; + +export const auth = defineAuth({ + name: 'myProjectFiles', + loginWith: { email: true }, + access: (allow) => ([ + allow.resource(demoFunction).to(['manageUsers']), + ]) +}); +``` + +This will grant `demoFunction` the ability to perform CRUD operations on users in the Cognito UserPool created as part of `defineAuth`. See below for the detailed list of permissions. + +When a backend function is granted access to authentication resources, it also receives an environment variable that contains the UserPool ID. This environment variable can be used in the function to make SDK calls to the UserPool. The environment variable is named `amplifyAuth_USERPOOL_ID` + +```ts title="amplify/functions/my-demo-function/handler.ts" +import { CognitoIdentityProviderClient, ListUsersCommand } from '@aws-sdk/client-cognito-identity-provider'; +import { env } from '@env/my-demo-function'; + +const cognitoIdentityProviderClient = new CognitoIdentityProviderClient(); + +export const handler = async(event) => { + await cognitoIdentityProviderClient.send( + new ListUsersCommand({ + UserPoolId: env.amplifyAuth_USERPOOL_ID, + Limit: 100 + }) + ) +} +``` +[Learn more about function resource access environment variables](../../functions/#resource-access) + +### List of actions + +|Action Name|Description|Cognito IAM Actions| +|-|-|-| +|manageUsers | Grants CRUD access to users in the UserPool |
  • cognito-idp:AdminConfirmSignUp
  • cognito-idp:AdminCreateUser
  • cognito-idp:AdminDeleteUser
  • cognito-idp:AdminDeleteUserAttributes
  • cognito-idp:AdminDisableUser
  • cognito-idp:AdminEnableUser
  • cognito-idp:AdminGetUser
  • cognito-idp:AdminListGroupsForUser
  • cognito-idp:AdminRespondToAuthChallenge
  • cognito-idp:AdminSetUserMFAPreference
  • cognito-idp:AdminSetUserSettings
  • cognito-idp:AdminUpdateUserAttributes
  • cognito-idp:AdminUserGlobalSignOut
| +|manageGroupMembership | Grants permission to add and remove users from groups |
  • cognito-idp:AdminAddUserToGroup
  • cognito-idp:AdminRemoveUserFromGroup
| +|manageUserDevices | Manages devices registered to users|
  • cognito-idp:AdminForgetDevice
  • cognito-idp:AdminGetDevice
  • cognito-idp:AdminListDevices
  • cognito-idp:AdminUpdateDeviceStatus
| +|managePasswordRecovery | Grants permission to reset user passwords |
  • cognito-idp:AdminResetUserPassword
  • cognito-idp:AdminSetUserPassword
| +|addUserToGroup | Grants permission to add any user to any group. |
  • cognito-idp:AdminAddUserToGroup
+|createUser | Grants permission to create new users and send welcome messages via email or SMS. |
  • cognito-idp:AdminCreateUser
+|deleteUser | Grants permission to delete any user |
  • cognito-idp:AdminDeleteUser
+|deleteUserAttributes | Grants permission to delete attributes from any user |
  • cognito-idp:AdminDeleteUserAttributes
+|disableUser | Grants permission to deactivate any user |
  • cognito-idp:AdminDisableUser
+|enableUser | Grants permission to activate any user |
  • cognito-idp:AdminEnableUser
+|forgetDevice | Grants permission to deregister any user's devices |
  • cognito-idp:AdminForgetDevice
+|getDevice | Grants permission to get information about any user's devices |
  • cognito-idp:AdminGetDevice
+|getUser | Grants permission to look up any user by user name |
  • cognito-idp:AdminGetUser
+|listDevices | Grants permission to list any user's remembered devices |
  • cognito-idp:AdminListDevices
+|listGroupsForUser | Grants permission to list the groups that any user belongs to |
  • cognito-idp:AdminListGroupsForUser
+|removeUserFromGroup | Grants permission to remove any user from any group |
  • cognito-idp:AdminRemoveUserFromGroup
+|resetUserPassword | Grants permission to reset any user's password |
  • cognito-idp:AdminResetUserPassword
+|setUserMfaPreference | Grants permission to set any user's preferred MFA method |
  • cognito-idp:AdminSetUserMFAPreference
+|setUserPassword | Grants permission to set any user's password |
  • cognito-idp:AdminSetUserPassword
+|setUserSettings | Grants permission to set user settings for any user |
  • cognito-idp:AdminSetUserSettings
+|updateDeviceStatus | Grants permission to update the status of any user's remembered devices |
  • cognito-idp:AdminUpdateDeviceStatus
+|updateUserAttributes | Grants permission to updates any user's standard or custom attributes |
  • cognito-idp:AdminUpdateUserAttributes
From cbacd40dc92ed98a87a17d50a216e1292bdc3b22 Mon Sep 17 00:00:00 2001 From: Katie Goines <30757403+katiegoines@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:20:13 -0700 Subject: [PATCH 57/91] fix: type platform as optional in Breadcrumbs component & add unit tests (#6912) * platform typed as optional * chore: add unit tests for Breadcrumbs * testing to include platform optional * fix: type platform as optional * add tests for override values --------- Co-authored-by: katiegoines --- .../__tests__/Breadcrumbs.test.tsx | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 src/components/Breadcrumbs/__tests__/Breadcrumbs.test.tsx diff --git a/src/components/Breadcrumbs/__tests__/Breadcrumbs.test.tsx b/src/components/Breadcrumbs/__tests__/Breadcrumbs.test.tsx new file mode 100644 index 00000000000..724a6c4abef --- /dev/null +++ b/src/components/Breadcrumbs/__tests__/Breadcrumbs.test.tsx @@ -0,0 +1,131 @@ +import * as React from 'react'; +import { render, screen } from '@testing-library/react'; +import { Breadcrumbs } from '../index'; + +describe('Breadcrumbs', () => { + it('should render the Breadcrumbs component', async () => { + const component = ( + + ); + render(component); + const breadcrumbs = await screen.findByLabelText('Breadcrumb'); + expect(breadcrumbs).toBeInTheDocument(); + }); + + it('should render links for Breadcrumbs including platform', async () => { + const component = ( + + ); + render(component); + const routeList = component.props.route.split('/').filter(function (el) { + return el != ''; + }); + const breadcrumbsNode = await screen.findByLabelText('Breadcrumb'); + const breadcrumbsList = + breadcrumbsNode.getElementsByClassName('breadcrumb__item'); + + let route = ''; + for (let i = 0; i < breadcrumbsList.length; i++) { + const breadcrumbLink = breadcrumbsList[i].getElementsByClassName( + 'amplify-breadcrumbs__link' + )[0]; + route = route + '/' + routeList[i]; + + expect(breadcrumbLink).toBeInTheDocument(); + expect(breadcrumbLink).toHaveAttribute('href', route); + } + }); + + it('should replace "prev" with applicable version in Breadcrumbs text', async () => { + const component = ( + + ); + render(component); + const routeList = component.props.route.split('/').filter(function (el) { + return el != ''; + }); + const breadcrumbsNode = await screen.findByLabelText('Breadcrumb'); + + const link = breadcrumbsNode + .getElementsByClassName('amplify-breadcrumbs__link') + .item(1); + + if ( + link?.getAttribute('href')?.includes('prev') && + (routeList[0] == 'javascript' || + routeList[0] == 'react' || + routeList[0] == 'react-native' || + routeList[0] == 'angular' || + routeList[0] == 'nextjs' || + routeList[0] == 'vue') + ) { + expect(link.textContent).toEqual('V5'); + } else if ( + link?.getAttribute('href')?.includes('prev') && + (routeList[0] == 'swift' || routeList[0] == 'android') + ) { + expect(link.textContent).toEqual('V1'); + } else if ( + link?.getAttribute('href')?.includes('prev') && + routeList[0] == 'flutter' + ) { + expect(link.textContent).toEqual('V0'); + } + }); + + it('should render links for Breadcrumbs for gen2', async () => { + const component = ( + + ); + render(component); + const routeList = component.props.route.split('/').filter(function (el) { + return el != ''; + }); + const breadcrumbsNode = await screen.findByLabelText('Breadcrumb'); + const breadcrumbsList = + breadcrumbsNode.getElementsByClassName('breadcrumb__item'); + + let route = ''; + for (let i = 0; i < breadcrumbsList.length; i++) { + const breadcrumbLink = breadcrumbsList[i].getElementsByClassName( + 'amplify-breadcrumbs__link' + )[0]; + route = route + '/' + routeList[i]; + + expect(breadcrumbLink).toBeInTheDocument(); + expect(breadcrumbLink).toHaveAttribute('href', route); + } + }); + + it('should render links for Breadcrumbs with no platform', async () => { + const component = ( + + ); + render(component); + const routeList = component.props.route.split('/').filter(function (el) { + return el != ''; + }); + const breadcrumbsNode = await screen.findByLabelText('Breadcrumb'); + const breadcrumbsList = + breadcrumbsNode.getElementsByClassName('breadcrumb__item'); + + let route = ''; + for (let i = 0; i < breadcrumbsList.length; i++) { + const breadcrumbLink = breadcrumbsList[i].getElementsByClassName( + 'amplify-breadcrumbs__link' + )[0]; + route = route + '/' + routeList[i]; + + expect(breadcrumbLink).toBeInTheDocument(); + expect(breadcrumbLink).toHaveAttribute('href', route); + } + }); +}); From e4aed00b00cf8e673ffe6cd4c5e9ac3aad9e1f81 Mon Sep 17 00:00:00 2001 From: Ujjwol Shrestha Date: Wed, 13 Mar 2024 07:05:37 +1100 Subject: [PATCH 58/91] fix: proper variable naming conventions for storage (#7033) fix: proper variable naming conventions for storage --- src/pages/gen2/build-a-backend/storage/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/gen2/build-a-backend/storage/index.mdx b/src/pages/gen2/build-a-backend/storage/index.mdx index 44a32b47c9c..6dce6583b60 100644 --- a/src/pages/gen2/build-a-backend/storage/index.mdx +++ b/src/pages/gen2/build-a-backend/storage/index.mdx @@ -278,7 +278,7 @@ Function triggers can be configured to enable event-based workflows when files a First, in your storage definition, add the following: ```ts title="amplify/storage/resource.ts" -export const auth = defineStorage({ +export const storage = defineStorage({ name: 'myProjectFiles', // highlight-start triggers: { From 79b112b7fb4035dbf692fcaf45c7650604493471 Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Wed, 13 Mar 2024 05:28:33 +0900 Subject: [PATCH 59/91] remove explicit file extension from gen2 imports (#6946) Co-authored-by: Tim Nguyen <54393192+timngyn@users.noreply.github.com> --- .../add-aws-services/custom-resources/index.mdx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx index 32240fa637b..e575ea29951 100644 --- a/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx +++ b/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx @@ -40,8 +40,8 @@ The AWS CDK comes with [many existing constructs](https://docs.aws.amazon.com/cd import * as sns from 'aws-cdk-lib/aws-sns'; import * as sqs from 'aws-cdk-lib/aws-sqs'; import { defineBackend } from '@aws-amplify/backend'; -import { auth } from './auth/resource.js'; -import { data } from './data/resource.js'; +import { auth } from './auth/resource'; +import { data } from './data/resource'; const backend = defineBackend({ auth, @@ -145,7 +145,7 @@ The Lambda function code for the `Publisher` is: // amplify/custom/CustomNotifications/publisher.ts import { PublishCommand, SNSClient } from '@aws-sdk/client-sns'; import type { Handler } from 'aws-lambda'; -import type { Message } from './resource.js'; +import type { Message } from './resource'; const client = new SNSClient({ region: process.env.AWS_REGION }); @@ -221,8 +221,8 @@ The `CustomNotifications` CDK construct can then be added to the Amplify `backen ```ts title="amplify/backend.ts" // amplify/backend.ts import { defineBackend } from '@aws-amplify/backend'; -import { auth } from './auth/resource.js'; -import { data } from './data/resource.js'; +import { auth } from './auth/resource'; +import { data } from './data/resource'; import { CustomNotifications } from './custom/CustomNotifications/resource'; const backend = defineBackend({ From 6ad97e54bfb259b695aa2d9edc5989a00bc58cfa Mon Sep 17 00:00:00 2001 From: Kevin Old Date: Tue, 12 Mar 2024 15:31:29 -0500 Subject: [PATCH 60/91] fix: Update diff code blocks to TypeScript with code highlights (#6905) --- .../auth/enable-sign-up/index.mdx | 17 +++++++++-------- .../build-a-backend/auth/manage-mfa/index.mdx | 13 +++++++------ .../auth/manage-user-profile/index.mdx | 17 +++++++++-------- .../auth/password-management/index.mdx | 9 +++++---- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/pages/gen2/build-a-backend/auth/enable-sign-up/index.mdx b/src/pages/gen2/build-a-backend/auth/enable-sign-up/index.mdx index 2b6ee47f551..52136a028e3 100644 --- a/src/pages/gen2/build-a-backend/auth/enable-sign-up/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/enable-sign-up/index.mdx @@ -42,20 +42,21 @@ Below is a high-level overview of the workflows for sign-up, sign-in, and sign-o User attributes such as `email` are used to identify your users. These are set up when you configure your Cognito User Pool and cannot be renamed or deleted after they are added. You can modify your backend authentication resource to include additional User attributes. -```diff title="amplify/auth/resource.ts" -// amplify/auth/resource.ts +```ts title="amplify/auth/resource.ts" import { defineAuth } from '@aws-amplify/backend'; export const auth = defineAuth({ loginWith: { email: true, }, -+ userAttributes: { -+ birthdate: { -+ required: false, -+ mutable: false, -+ }, -+ }, +// highlight-start + userAttributes: { + birthdate: { + required: false, + mutable: false, + }, + }, +// highlight-end }); ``` diff --git a/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx b/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx index 1d8d1741504..174a978284d 100644 --- a/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx @@ -23,18 +23,19 @@ Before you begin, you will need: Use `defineAuth` to enable MFA for your app. The example below is setting up MFA with TOTP but not SMS as you can see that the phone number is not a required attribute. If you are using SMS, then the `PhoneNumber` attribute must be `true`. -```diff title="amplify/auth/resource.ts" -// amplify/auth/resource.ts +```ts title="amplify/auth/resource.ts" import { defineAuth } from '@aws-amplify/backend'; export const auth = defineAuth({ loginWith: { email: true }, -+ multifactor: { -+ mode: 'optional', -+ totp: true -+ } +// highlight-start + multifactor: { + mode: 'optional', + totp: true + } +// highlight-end }); ``` diff --git a/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx b/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx index db87ead9af2..f4a51d5158d 100644 --- a/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx @@ -51,20 +51,21 @@ For more information, visit the [AWS documentation for Amazon Cognito with a lis The following is an example on how to add a user attribute to your authentication resources. -```diff title="amplify/auth/resource.ts" -// amplify/auth/resource.ts +```ts title="amplify/auth/resource.ts" import { defineAuth } from '@aws-amplify/backend'; export const auth = defineAuth({ loginWith: { email: true, }, -+ userAttributes: { -+ birthdate: { -+ required: false, -+ mutable: false, -+ }, -+ }, +// highlight-start + userAttributes: { + birthdate: { + required: false, + mutable: false, + }, + }, +// highlight-end }); ``` diff --git a/src/pages/gen2/build-a-backend/auth/password-management/index.mdx b/src/pages/gen2/build-a-backend/auth/password-management/index.mdx index 7ab82556678..bc6d8ea7256 100644 --- a/src/pages/gen2/build-a-backend/auth/password-management/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/password-management/index.mdx @@ -12,7 +12,7 @@ export function getStaticProps(context) { }; } -Amplify Auth provides a secure way for your users to change their password or recover a forgotten password. +Amplify Auth provides a secure way for your users to change their password or recover a forgotten password. If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). @@ -31,15 +31,16 @@ By default, your users can retrieve access to their accounts if they forgot thei You can always change the channel used by your authentication resources by overriding the following setting. -```diff title="amplify/auth/resource.ts" -// amplify/auth/resource.ts +```ts title="amplify/auth/resource.ts" import { defineAuth } from '@aws-amplify/backend'; export const auth = defineAuth({ loginWith: { email: true }, -+ accountRecovery: 'EMAIL_ONLY' +// highlight-start + accountRecovery: 'EMAIL_ONLY' +// highlight-end }); ``` From b4ad8fe23f058a4d47638f78bd09de7d7b80512d Mon Sep 17 00:00:00 2001 From: Edward Foyle Date: Tue, 12 Mar 2024 14:44:20 -0700 Subject: [PATCH 61/91] add docs on granting defineFunction access to defineData (#7029) --- .../index.mdx | 150 +++++++++++++++--- 1 file changed, 130 insertions(+), 20 deletions(-) diff --git a/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx b/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx index a18903bcc53..7b4afecd7bf 100644 --- a/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx +++ b/src/pages/gen2/build-a-backend/data/customize-authz/grant-lambda-function-access-to-api/index.mdx @@ -1,6 +1,7 @@ export const meta = { title: 'Grant Lambda function access to API and Data', - description: "Amplify Data uses a 'deny-by-default' authorization model. To allow access for Lambda functions or other services authenticated via IAM, their IAM role names must be explicitly listed in the `allowListedRoleNames` property when defining the Data resource. This grants privileged access based on their IAM policy rather than their `.authorization()` rules." + description: + "Amplify Data uses a 'deny-by-default' authorization model. Function access must be explicitly defined in the schema." }; export function getStaticProps(context) { @@ -11,35 +12,144 @@ export function getStaticProps(context) { }; } -The IAM execution role for Lambda functions does not automatically grant access to the Amplify Data API. This is because the API operates on a "deny-by-default" permission model. Access must be explicitly granted. - -To grant an external AWS resource or an IAM role access to the Amplify Data API, you need to explicitly list the IAM roles by adding them to `allowListedRoleNames` property. +Function access to `defineData` can be configured using an authorization rule on the schema object. ```ts title="amplify/data/resource.ts" -// amplify/data/resource.ts -import { a, defineData, type ClientSchema } from '@aws-amplify/backend'; +import { + a, + defineData, + defineFunction, + type ClientSchema +} from '@aws-amplify/backend'; + +const functionWithDataAccess = defineFunction({ + entry: '../functions/data-access.ts' +}); -const schema = a.schema({ - Todo: a - .model({ +const schema = a + .schema({ + Todo: a.model({ name: a.string(), - description: a.string(), + description: a.string() }) - .authorization([a.allow.public()]), -}); + }) + // highlight-next-line + .authorization([a.allow.resource(functionWithDataAccess)]); export type Schema = ClientSchema; export const data = defineData({ - schema, - authorizationModes: { - defaultAuthorizationMode: 'apiKey', + schema +}); +``` + +The object returned from `defineFunction` can be passed directly to `a.allow.resource()` in the schema authorization rules. This will grant the function the ability to execute Query, Mutation, and Subscription operations against the GraphQL API. Use the `.to()` method to narrow down access to one or more operations. + +```ts +const schema = a + .schema({ + Todo: a.model({ + name: a.string(), + description: a.string() + }) + }) + // highlight-start + .authorization([ + a.allow.resource(functionWithDataAccess).to(['query', 'listen']) + ]); // allow query and subscription operations but not mutations +// highlight-end +``` + +When configuring function access, the function will be provided the API endpoint as an environment variable named `_GRAPHQL_ENDPOINT`. The default name is `amplifyData_GRAPHQL_ENDPOINT` unless you have specified a different name in `defineData`. + + + +Function access can only be configured on the schema object. It cannot be configured on individual models or fields. + + + +## Access the API using `aws-amplify` + + + +**Under active development:** Configuring the `aws-amplify` data client is under active development. The current experience has rough edges and may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose + + + +In the handler file for your function, configure the Amplify data client - // Pass in the Lambda Execution Role names to grant full read/write access to the API - // if their IAM policies permit it. - allowListedRoleNames: ['lambdaRole'], +```ts title="amplify/functions/data-access.ts" +import { Amplify } from 'aws-amplify'; +import { generateClient } from 'aws-amplify/data'; +import { Schema } from '../data/resource'; +import { env } from '@env/'; // replace with your function name +// highlight-start +// see instructions below on copying this file +import { modelIntrospection } from './amplifyconfiguration.json'; +// highlight-end + +Amplify.configure( + { + API: { + GraphQL: { + endpoint: env._GRAPHQL_ENDPOINT, // replace with your defineData name + region: env.AWS_REGION, + defaultAuthMode: 'iam', + modelIntrospection: modelIntrospection as never + } + } }, -}); + { + Auth: { + credentialsProvider: { + getCredentialsAndIdentityId: async () => ({ + credentials: { + accessKeyId: env.AWS_ACCESS_KEY_ID, + secretAccessKey: env.AWS_SECRET_ACCESS_KEY, + sessionToken: env.AWS_SESSION_TOKEN, + }, + }), + clearCredentialsAndIdentityId: () => { + /* noop */ + }, + }, + }, + } +); + +const dataClient = generateClient(); + +export const handler = async (event) => { + // your function code goes here +} ``` -These "Allow-listed Roles" have special access privileges that are scoped based on their [IAM policy](https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html#aws-iam-authorization) instead of any particular `.authorization()` rule. +The `amplifyconfiguration.json` file is not automatically accessible to your function. To include it with your function, you must first do a deployment of your backend using either `npx amplify sandbox` or Amplify hosting. Then copy the `amplifyconfiguration.json` file that is placed in your project root into your function directory. Now you can import the file into your function as shown above. + + + +**Note:** Whenever you update your data model, you will need to copy the `amplifyconfiguration.json` file again. + + + +Once you have configured the Amplify data client, you can take full advantage of the automatic typing and other model operations on the data client. The following code creates a todo and then lists all todos. + +```ts title="amplify/functions/data-access.ts" +const dataClient = generateClient(); + +export const handler = async (event) => { + await dataClient.models.Todo.create({ + content: 'learn how to use the data client in a function' + }); + + const { data: allTodos, errors } = await dataClient.models.Todo.list(); + + if (errors) { + throw new Error(errors.join(', ')); + } + + return { + allTodos + }; +}; +``` From 5e1c7385859daeed574afdc00fde293ca0365b32 Mon Sep 17 00:00:00 2001 From: Jay Raval Date: Tue, 12 Mar 2024 18:23:45 -0400 Subject: [PATCH 62/91] standardize Gen 2 wording (#7036) --- .../gen2/quickstart/build-a-backend.mdx | 2 +- .../add-aws-services/analytics/index.mdx | 2 +- .../custom-resources/index.mdx | 2 +- .../add-aws-services/geo/index.mdx | 2 +- .../auth/add-social-provider/index.mdx | 4 ++-- .../auth/admin-actions/index.mdx | 2 +- .../auth/auth-events/index.mdx | 2 +- .../auth/delete-user-account/index.mdx | 2 +- .../build-a-backend/auth/manage-mfa/index.mdx | 4 ++-- .../auth/manage-user-profile/index.mdx | 2 +- .../auth/manage-user-session/index.mdx | 2 +- .../auth/password-management/index.mdx | 2 +- .../cross-account-deployments/index.mdx | 24 +++++++++---------- .../custom-pipelines/index.mdx | 4 ++-- .../mono-and-multi-repos/index.mdx | 2 +- .../fullstack-branching/monorepos/index.mdx | 2 +- .../secrets-and-vars/index.mdx | 6 ++--- .../sandbox-environments/features/index.mdx | 4 ++-- .../sandbox-environments/setup/index.mdx | 2 +- .../gen2/how-amplify-works/concepts/index.mdx | 10 ++++---- .../gen2/how-amplify-works/faq/index.mdx | 8 +++---- .../reference/amplifyconfiguration/index.mdx | 2 +- .../gen2/reference/cli-commands/index.mdx | 2 +- .../reference/project-structure/index.mdx | 2 +- src/pages/gen2/reference/telemetry/index.mdx | 4 ++-- src/pages/gen2/start/configure/index.mdx | 6 ++--- src/pages/gen2/start/index.mdx | 2 +- .../gen2/start/manual-installation/index.mdx | 4 ++-- src/pages/gen2/start/quickstart/index.mdx | 2 +- .../index.mdx | 2 +- .../index.mdx | 4 ++-- .../quickstart/nextjs-pages-router/index.mdx | 2 +- .../start/quickstart/vite-react-app/index.mdx | 2 +- 33 files changed, 62 insertions(+), 62 deletions(-) diff --git a/src/fragments/gen2/quickstart/build-a-backend.mdx b/src/fragments/gen2/quickstart/build-a-backend.mdx index 86edba24001..cba4f141643 100644 --- a/src/fragments/gen2/quickstart/build-a-backend.mdx +++ b/src/fragments/gen2/quickstart/build-a-backend.mdx @@ -1,6 +1,6 @@ ## Build a backend -Next, we will add data and auth capabilities to the app. In Amplify (Gen 2), the `resource.ts` files are where you _define_ the corresponding backend resource and details about it. +Next, we will add data and auth capabilities to the app. In Amplify Gen 2, the `resource.ts` files are where you _define_ the corresponding backend resource and details about it. ### Add data diff --git a/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx index 96e0e428a31..c7a8c394167 100644 --- a/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx +++ b/src/pages/gen2/build-a-backend/add-aws-services/analytics/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { -**Under active development:** The `addOutput` method for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose +**Under active development:** The `addOutput` method for Amplify Gen 2 is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose diff --git a/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx index e575ea29951..5786b6dd772 100644 --- a/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx +++ b/src/pages/gen2/build-a-backend/add-aws-services/custom-resources/index.mdx @@ -18,7 +18,7 @@ Custom resources allow you to integrate any AWS service into an Amplify backend.
-With Amplify (Gen 2), you can add custom AWS resources to an Amplify app using the [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html), which is installed by default as part of the [`create-amplify`](https://www.npmjs.com/package/create-amplify) workflow. The AWS CDK is an open source software development framework that defines your cloud application resources using familiar programming languages, such as TypeScript. +With Amplify Gen 2, you can add custom AWS resources to an Amplify app using the [AWS Cloud Development Kit (AWS CDK)](https://docs.aws.amazon.com/cdk/latest/guide/home.html), which is installed by default as part of the [`create-amplify`](https://www.npmjs.com/package/create-amplify) workflow. The AWS CDK is an open source software development framework that defines your cloud application resources using familiar programming languages, such as TypeScript. The AWS CDK can be used within an Amplify app to add custom resources and configurations beyond what Amplify supports out of the box. For example, a developer could use CDK to hook up a Redis cache, implement custom security rules, deploy containers on AWS Fargate, or use any other AWS service. diff --git a/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx b/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx index 521ba0b0110..70291e94d3b 100644 --- a/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx +++ b/src/pages/gen2/build-a-backend/add-aws-services/geo/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { -**Under active development:** The `addOutput` method for Amplify (Gen 2) is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose +**Under active development:** The `addOutput` method for Amplify Gen 2 is under active development. The experience may change between versions of `@aws-amplify/backend`. Try it out and provide feedback at https://github.com/aws-amplify/amplify-backend/issues/new/choose diff --git a/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx b/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx index 579376b824f..d61f6fcbe97 100644 --- a/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/add-social-provider/index.mdx @@ -15,7 +15,7 @@ You can enable the use of sign-in with social providers using the `amplify/auth/ In this guide we will review how you can add sign-in with social providers by first setting up your developer account with the provider. After this step, you can then configure your Auth category and integrate social sign-in with your app. -If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). +If you have not yet created an Amplify Gen 2 app, visit the [quickstart](/gen2/start/quickstart). @@ -283,7 +283,7 @@ export const auth = defineAuth({ ## Set up your frontend -If you are using the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration#social-providers) with Amplify (Gen 2), this feature works without any additional code. The guide below is for writing your own implementation. +If you are using the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration#social-providers) with Amplify Gen 2, this feature works without any additional code. The guide below is for writing your own implementation. Enabling a signing in of a user with a social provider can be done as shown in the following code snippet. diff --git a/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx b/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx index 1518730c989..d1fc4dda59c 100644 --- a/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/admin-actions/index.mdx @@ -11,4 +11,4 @@ export function getStaticProps(context) { }; } -We do not yet have out-of-the-box support for Admin queries in Amplify (Gen 2). You will have to a create a new [AWS Lambda](https://aws.amazon.com/lambda/) resource, and provide it with the necessary permissions to access the [Amazon Cognito Admin Actions](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_Operations.html) behind the scenes. +We do not yet have out-of-the-box support for Admin queries in Amplify Gen 2. You will have to a create a new [AWS Lambda](https://aws.amazon.com/lambda/) resource, and provide it with the necessary permissions to access the [Amazon Cognito Admin Actions](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_Operations.html) behind the scenes. diff --git a/src/pages/gen2/build-a-backend/auth/auth-events/index.mdx b/src/pages/gen2/build-a-backend/auth/auth-events/index.mdx index 926bd6fdb2a..74e0019361d 100644 --- a/src/pages/gen2/build-a-backend/auth/auth-events/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/auth-events/index.mdx @@ -14,7 +14,7 @@ export function getStaticProps(context) { Tapping into auth events allows you to react to user flows in real time and trigger custom business logic. For example, you may want to capture data, synchronize your app's state, and personalize the user's experience. You can listen to and respond to events across the Auth lifecycle such as sign-in and sign-out. In this guide we will describe how you can listen to these auth events. -If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). +If you have not yet created an Amplify Gen 2 app, visit the [quickstart](/gen2/start/quickstart). ## Expose hub events triggered in response to auth actions diff --git a/src/pages/gen2/build-a-backend/auth/delete-user-account/index.mdx b/src/pages/gen2/build-a-backend/auth/delete-user-account/index.mdx index f99de6514bf..1e8c7769327 100644 --- a/src/pages/gen2/build-a-backend/auth/delete-user-account/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/delete-user-account/index.mdx @@ -15,7 +15,7 @@ export function getStaticProps(context) { Empowering users to delete their account can improve trust and transparency. You can programmatically enable self-service account deletion with Amplify Auth. -If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). +If you have not yet created an Amplify Gen 2 app, visit the [quickstart](/gen2/start/quickstart). ## Allow my users to delete their account diff --git a/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx b/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx index 174a978284d..f1668bc7e4e 100644 --- a/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/manage-mfa/index.mdx @@ -63,7 +63,7 @@ When enabling MFA you will have two key decisions to make: If you are using the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator) - with Amplify (Gen 2), this feature works without any additional code. The + with Amplify Gen 2, this feature works without any additional code. The guide below is for writing your own implementation. @@ -262,7 +262,7 @@ async function handleUpdateMFAPreference() { If you are using the [Authenticator component](https://ui.docs.amplify.aws/react/connected-components/authenticator) - with Amplify (Gen 2), this feature works without any additional code. The + with Amplify Gen 2, this feature works without any additional code. The guide below is for writing your own implementation. diff --git a/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx b/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx index f4a51d5158d..9947852afda 100644 --- a/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/manage-user-profile/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { User profile management helps you keep your applications secure while also personalizing your app experiences. In this guide we will review how you can enable your users to personalize their profile and verify their contact information. This includes outlining how you can set up user attributes, verify them, and allow your users to delete them when necessary. -If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). +If you have not yet created an Amplify Gen 2 app, visit the [quickstart](/gen2/start/quickstart). ## Set up user attributes diff --git a/src/pages/gen2/build-a-backend/auth/manage-user-session/index.mdx b/src/pages/gen2/build-a-backend/auth/manage-user-session/index.mdx index 3dcd275153f..17d46e5aebd 100644 --- a/src/pages/gen2/build-a-backend/auth/manage-user-session/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/manage-user-session/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { Amplify Auth provides access to current user sessions and tokens to help you retrieve your user's information to determine if they are signed in with a valid session and control their access to your app. You can manage tokens and expiration times and revoke sessions. In this guide we will review how to retrieve your user's session and understand what token management options are available. -If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). +If you have not yet created an Amplify Gen 2 app, visit the [quickstart](/gen2/start/quickstart). ## Retrieve your current authenticated user diff --git a/src/pages/gen2/build-a-backend/auth/password-management/index.mdx b/src/pages/gen2/build-a-backend/auth/password-management/index.mdx index bc6d8ea7256..4e6bfacd3f7 100644 --- a/src/pages/gen2/build-a-backend/auth/password-management/index.mdx +++ b/src/pages/gen2/build-a-backend/auth/password-management/index.mdx @@ -14,7 +14,7 @@ export function getStaticProps(context) { Amplify Auth provides a secure way for your users to change their password or recover a forgotten password. -If you have not yet created an Amplify (Gen 2) app, visit the [quickstart](/gen2/start/quickstart). +If you have not yet created an Amplify Gen 2 app, visit the [quickstart](/gen2/start/quickstart). ## Understand password default settings diff --git a/src/pages/gen2/deploy-and-host/fullstack-branching/cross-account-deployments/index.mdx b/src/pages/gen2/deploy-and-host/fullstack-branching/cross-account-deployments/index.mdx index daab51b13fa..f76d0e9df1b 100644 --- a/src/pages/gen2/deploy-and-host/fullstack-branching/cross-account-deployments/index.mdx +++ b/src/pages/gen2/deploy-and-host/fullstack-branching/cross-account-deployments/index.mdx @@ -12,7 +12,7 @@ export function getStaticProps(context) { }; } -This guide walks you through how to create a trunk-based, multi-region deployment pipeline for applications built using AWS Amplify (Gen 2). We will be using Amazon CodeCatalyst and AWS Amplify Hosting in this guide, but you can choose any CI/CD provider. +This guide walks you through how to create a trunk-based, multi-region deployment pipeline for applications built using AWS Amplify Gen 2. We will be using Amazon CodeCatalyst and AWS Amplify Hosting in this guide, but you can choose any CI/CD provider. @@ -24,31 +24,31 @@ This guide walks you through how to create a trunk-based, multi-region deploymen Please refer to this Amazon CodeCatalyst [guide](https://docs.aws.amazon.com/codecatalyst/latest/userguide/setting-up-topnode.html) for a detailed step-by-step walkthrough to set up your [space](https://docs.aws.amazon.com/codecatalyst/latest/userguide/spaces.html). -### Step 2: Deploy an app using Amplify (Gen 2) +### Step 2: Deploy an app using Amplify Gen 2 - Sign in to the [AWS Management Console](https://console.aws.amazon.com/). -- Navigate to the [app creation page](https://us-west-2.console.aws.amazon.com/amplify/create) for Amplify (Gen 2). +- Navigate to the [app creation page](https://us-west-2.console.aws.amazon.com/amplify/create) for Amplify Gen 2. - Select the **Next.js-Amplify starter** app, then select **Next**. - Review the details on the **Create Git Repository** page, then select **Save and deploy**. -- Done! You have successfully deployed a Gen-2 app. You can review the status of the app deployment in the Amplify (Gen 2) console. +- Done! You have successfully deployed a Gen 2 app. You can review the status of the app deployment in the Amplify Gen 2 console. -![Screenshot of completed deployment in AWS Amplify (Gen 2) console](images/gen2/cross-account-deployments/pipeline1.png) +![Screenshot of completed deployment in AWS Amplify Gen 2 console](images/gen2/cross-account-deployments/pipeline1.png) ### Step 3: Update build specification Add the `npx amplify generate config --branch $AWS_BRANCH --app-id $AWS_APP_ID` command to the build spec and comment out the `npx amplify pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID` command. `amplify pipeline-deploy` runs a script to deploy backend updates, while `amplify generate config` fetches the latest `amplifyconfiguration.json` for the specified environment. -![Screenshot of Build image settings section in AWS Amplify (Gen 2) console, with details about the app build specification](images/gen2/cross-account-deployments/pipeline10.png) +![Screenshot of Build image settings section in AWS Amplify Gen 2 console, with details about the app build specification](images/gen2/cross-account-deployments/pipeline10.png) ### Step 4: Disable automatic builds on the branch -You can configure Amplify to disable automatic builds on every code commit. Navigate to the app in the Amplify (Gen 2) console. Under **App settings**, select **Repository settings**. From the **Branches** section, select the branch and then choose **Disable auto build** from the **Actions** dropdown menu. +You can configure Amplify to disable automatic builds on every code commit. Navigate to the app in the Amplify Gen 2 console. Under **App settings**, select **Repository settings**. From the **Branches** section, select the branch and then choose **Disable auto build** from the **Actions** dropdown menu. -![Screenshot of the Repository settings section in the Amplify (Gen 2) console, highlighting the selection of Disable auto build in the Actions dropdown menu](images/gen2/cross-account-deployments/pipeline9.png) +![Screenshot of the Repository settings section in the Amplify Gen 2 console, highlighting the selection of Disable auto build in the Actions dropdown menu](images/gen2/cross-account-deployments/pipeline9.png) ### Step 5: Create an incoming webhook -You can set up an incoming webhook to trigger a build without committing code to your Git repository. Currently, the Amplify (Gen 2) console does not support creation of incoming webhooks. However, you can create a webhook for a Gen-2 app using the AWS CLI by running the [`create-webhook`](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/amplify/create-webhook.html) command: +You can set up an incoming webhook to trigger a build without committing code to your Git repository. Currently, the Amplify Gen 2 console does not support creation of incoming webhooks. However, you can create a webhook for a Gen 2 app using the AWS CLI by running the [`create-webhook`](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/amplify/create-webhook.html) command: ```bash aws amplify create-webhook --app-id --branch-name --region @@ -81,7 +81,7 @@ Please refer to this Amazon CodeCatalyst [guide](https://docs.aws.amazon.com/cod -**Note**: When creating your project, select the **amplify-nextjs-starter-app** GitHub repository, which we used to deploy the Gen-2 app in Step 2. +**Note**: When creating your project, select the **amplify-nextjs-starter-app** GitHub repository, which we used to deploy the Gen 2 app in Step 2. @@ -97,7 +97,7 @@ Navigate to the CodeCatalyst space created as part of Step 1, select **Settings* ![Screenshot of CodeCatalyst console showing details of the Associate AWS account section](images/gen2/cross-account-deployments/pipeline12.png) -You will also need to create an IAM role in the target AWS account which will be assumed by the `staging` environment to perform actions and deploy resources in the `production` environment. As a best practice, we recommend attaching the [`AmplifyBackendDeployFullAccess`](https://docs.aws.amazon.com/amplify/latest/userguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmplifyBackendDeployFullAccess) AWS managed policy to the IAM role as it contains all the required permissions to deploy Gen-2 resources in your account. You can learn more about adding IAM roles to account connections in the CodeCatalyst [documentation](https://docs.aws.amazon.com/codecatalyst/latest/userguide/ipa-connect-account-addroles.html). +You will also need to create an IAM role in the target AWS account which will be assumed by the `staging` environment to perform actions and deploy resources in the `production` environment. As a best practice, we recommend attaching the [`AmplifyBackendDeployFullAccess`](https://docs.aws.amazon.com/amplify/latest/userguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmplifyBackendDeployFullAccess) AWS managed policy to the IAM role as it contains all the required permissions to deploy Gen 2 resources in your account. You can learn more about adding IAM roles to account connections in the CodeCatalyst [documentation](https://docs.aws.amazon.com/codecatalyst/latest/userguide/ipa-connect-account-addroles.html). ### Step 9: Create a workflow in the Amazon CodeCatalyst project @@ -166,4 +166,4 @@ Next, you can review the result of the workflow run from the **Runs** tab: ![Screenshot of CodeCatalyst console showing the Workflows section, focusing on the Runs tab](images/gen2/cross-account-deployments/pipeline11.png) -Done! You have successfully set up a custom cross-account pipeline to deploy your frontend and backend for apps built using Amplify (Gen 2). To summarize, this custom pipeline will enable you to deploy your backend initially with your `staging` environment using `amplify pipeline-deploy` in the CodeCatalyst workflow and `amplify generate config` will generate the `amplifyconfiguration.json` file for the `main` branch. Amplify Hosting will not deploy backend resources as part of the build and instead will use the deployed backend resources from the `main` branch. Once the staging environment deploys successfully, a similar process will be followed to deploy your `production` environment in a different AWS account. +Done! You have successfully set up a custom cross-account pipeline to deploy your frontend and backend for apps built using Amplify Gen 2. To summarize, this custom pipeline will enable you to deploy your backend initially with your `staging` environment using `amplify pipeline-deploy` in the CodeCatalyst workflow and `amplify generate config` will generate the `amplifyconfiguration.json` file for the `main` branch. Amplify Hosting will not deploy backend resources as part of the build and instead will use the deployed backend resources from the `main` branch. Once the staging environment deploys successfully, a similar process will be followed to deploy your `production` environment in a different AWS account. diff --git a/src/pages/gen2/deploy-and-host/fullstack-branching/custom-pipelines/index.mdx b/src/pages/gen2/deploy-and-host/fullstack-branching/custom-pipelines/index.mdx index 00f968535d3..9d12fc625ca 100644 --- a/src/pages/gen2/deploy-and-host/fullstack-branching/custom-pipelines/index.mdx +++ b/src/pages/gen2/deploy-and-host/fullstack-branching/custom-pipelines/index.mdx @@ -12,13 +12,13 @@ export function getStaticProps(context) { } -While building with Amplify CI/CD offers benefits such as zero-config setup, fullstack previews, centralized secrets management, Amplify (Gen 2) makes it easy to integrate fullstack CI/CD into your custom pipelines (for example, AWS CodePipeline, Amazon CodeCatalyst, GitHub Actions, and more). +While building with Amplify CI/CD offers benefits such as zero-config setup, fullstack previews, centralized secrets management, Amplify Gen 2 makes it easy to integrate fullstack CI/CD into your custom pipelines (for example, AWS CodePipeline, Amazon CodeCatalyst, GitHub Actions, and more). ## Set up backend deployments You can set up your backend deployments using the following steps: -1. Create an Amplify app by connecting a fullstack Gen-2 branch from your Git repository. This is a one time setup as for subsequent deployments, we will be using a custom pipeline. +1. Create an Amplify app by connecting a fullstack Gen 2 branch from your Git repository. This is a one time setup as for subsequent deployments, we will be using a custom pipeline. 2. Disable Auto-build for your branch. This will ensure code commits to your branch will not trigger a build. diff --git a/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx b/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx index d581894c164..c2f275eee7a 100644 --- a/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx +++ b/src/pages/gen2/deploy-and-host/fullstack-branching/mono-and-multi-repos/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { } -You might have different frontend and backend teams that maintain their own repositories. With Amplify (Gen 2), you can deploy repositories that have backend-only code, so frontend and backend teams can operate independently of each other. +You might have different frontend and backend teams that maintain their own repositories. With Amplify Gen 2, you can deploy repositories that have backend-only code, so frontend and backend teams can operate independently of each other. ## Deploy the backend app diff --git a/src/pages/gen2/deploy-and-host/fullstack-branching/monorepos/index.mdx b/src/pages/gen2/deploy-and-host/fullstack-branching/monorepos/index.mdx index 49f06acbe4c..192aca361f5 100644 --- a/src/pages/gen2/deploy-and-host/fullstack-branching/monorepos/index.mdx +++ b/src/pages/gen2/deploy-and-host/fullstack-branching/monorepos/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { Some teams choose a monorepo approach, or single repositories that contain multiple packages or components to simplify the deployment process for shared libraries and components. Without a monorepo, you have to deploy each package individually, keep track of package versions and dependencies across packages, and ensure version compatibility. This can become exponentially more complex as the number of packages grows. With a monorepo, all packages and dependencies are contained within a single repository. -Amplify (Gen 2) supports monorepo workflows for fullstack builds with monorepo tools such as Nx and yarn workspaces. When building with Gen 2, we recommend creating the `amplify/` folder in a shared workspace. We will use the following example for this guide: +Amplify Gen 2 supports monorepo workflows for fullstack builds with monorepo tools such as Nx and yarn workspaces. When building with Gen 2, we recommend creating the `amplify/` folder in a shared workspace. We will use the following example for this guide: ```text ├── apps/ diff --git a/src/pages/gen2/deploy-and-host/fullstack-branching/secrets-and-vars/index.mdx b/src/pages/gen2/deploy-and-host/fullstack-branching/secrets-and-vars/index.mdx index e2000bd49be..8fcfda9e50e 100644 --- a/src/pages/gen2/deploy-and-host/fullstack-branching/secrets-and-vars/index.mdx +++ b/src/pages/gen2/deploy-and-host/fullstack-branching/secrets-and-vars/index.mdx @@ -12,10 +12,10 @@ export function getStaticProps(context) { } -Amplify (Gen 2) offers centralized management of secrets and environment variables for all fullstack branches. Secrets allow you to securely configure environment-specific values like social sign-in keys, function environment variables, function secrets, and other sensitive data needed by your application across environments. +Amplify Gen 2 offers centralized management of secrets and environment variables for all fullstack branches. Secrets allow you to securely configure environment-specific values like social sign-in keys, function environment variables, function secrets, and other sensitive data needed by your application across environments. - - In Amplify (Gen 1), you need to define environment variables and secrets using the CLI and store keys in both AWS Parameter Store and a local `team-provider.json` file. We have streamlined this workflow in Amplify (Gen 2) by centralizing the management of secrets and environment variables in the Amplify console. + + In Amplify Gen 1, you need to define environment variables and secrets using the CLI and store keys in both AWS Parameter Store and a local `team-provider.json` file. We have streamlined this workflow in Amplify Gen 2 by centralizing the management of secrets and environment variables in the Amplify console. ## Set secrets diff --git a/src/pages/gen2/deploy-and-host/sandbox-environments/features/index.mdx b/src/pages/gen2/deploy-and-host/sandbox-environments/features/index.mdx index 7536c5a8dad..8d57ec06dce 100644 --- a/src/pages/gen2/deploy-and-host/sandbox-environments/features/index.mdx +++ b/src/pages/gen2/deploy-and-host/sandbox-environments/features/index.mdx @@ -21,7 +21,7 @@ Sandbox environments include additional features for managing secrets, deploying Secrets set in a sandbox do not show up in the Amplify Console. You can view them in the AWS Systems Manager (SSM) Parameter Store console. -Amplify (Gen 2) offers secure secret storage to manage sensitive data like API keys and database credentials. Secrets are similar to environment variables, but they are encrypted AWS Systems Manager Parameter Store key value pairs. Secrets are stored in AWS Parameter Store with the following naming convention: `/amplify///`. +Amplify Gen 2 offers secure secret storage to manage sensitive data like API keys and database credentials. Secrets are similar to environment variables, but they are encrypted AWS Systems Manager Parameter Store key value pairs. Secrets are stored in AWS Parameter Store with the following naming convention: `/amplify///`. ### Set secrets @@ -124,7 +124,7 @@ npx amplify generate config --app-id --branch main --format ["m ## Generate client codegen - Amplify (Gen 2) introduces a fully typed experience for data that no longer requires an explicit codegen step, unlike in Amplify (Gen 1). You will only need this command if you are building a mobile app or have Gen-1 requirements. + Amplify Gen 2 introduces a fully typed experience for data that no longer requires an explicit codegen step, unlike in Amplify Gen 1. You will only need this command if you are building a mobile app or have Gen 1 requirements. Codegen generates native code for Swift (iOS), Java (Android), and JavaScript that represents your GraphQL API's data models. It can also generate GraphQL statements (queries, mutations, and subscriptions) so that you don't have to manually code them. diff --git a/src/pages/gen2/deploy-and-host/sandbox-environments/setup/index.mdx b/src/pages/gen2/deploy-and-host/sandbox-environments/setup/index.mdx index dc037556ca5..56215619f83 100644 --- a/src/pages/gen2/deploy-and-host/sandbox-environments/setup/index.mdx +++ b/src/pages/gen2/deploy-and-host/sandbox-environments/setup/index.mdx @@ -23,7 +23,7 @@ Cloud sandbox environments are not intended for production workloads. ## Create a new sandbox environment -You can set up a new sandbox environment on your machine once you have an Amplify app set up. If you have not yet created an Amplify (Gen 2) app, visit the [Quickstart](/gen2/start/quickstart). +You can set up a new sandbox environment on your machine once you have an Amplify app set up. If you have not yet created an Amplify Gen 2 app, visit the [Quickstart](/gen2/start/quickstart). First, open the terminal and run the following command: diff --git a/src/pages/gen2/how-amplify-works/concepts/index.mdx b/src/pages/gen2/how-amplify-works/concepts/index.mdx index db9182947b0..af786570772 100644 --- a/src/pages/gen2/how-amplify-works/concepts/index.mdx +++ b/src/pages/gen2/how-amplify-works/concepts/index.mdx @@ -13,7 +13,7 @@ export function getStaticProps(context) { } -AWS Amplify (Gen 2) introduces a TypeScript-based, code-first developer experience (DX) for defining backends. The Gen-2 DX offers a unified Amplify developer experience with the same hosting, backend, and UI-building capabilities previously available in CLI and Studio, but with a code-first approach. Amplify empowers frontend developers to deploy cloud infrastructure by simply expressing their app’s data model, business logic, authentication, and authorization rules completely in TypeScript. Amplify automatically configures the correct cloud resources and removes the requirement to stitch together underlying AWS services. +AWS Amplify Gen 2 introduces a TypeScript-based, code-first developer experience (DX) for defining backends. The Gen 2 DX offers a unified Amplify developer experience with the same hosting, backend, and UI-building capabilities previously available in CLI and Studio, but with a code-first approach. Amplify empowers frontend developers to deploy cloud infrastructure by simply expressing their app’s data model, business logic, authentication, and authorization rules completely in TypeScript. Amplify automatically configures the correct cloud resources and removes the requirement to stitch together underlying AWS services. ## Capabilities @@ -21,25 +21,25 @@ Just like with the CLI and Studio in Gen 1, you can use Amplify for end-to-end f ### Build fullstack apps with TypeScript -With the Gen-2 DX, you can provision backend infrastructure by authoring TypeScript. In the following diagram, the box at the bottom (outlined in pink), highlights the main difference in how you provision infrastructure compared to Gen 1. In Gen 1, you would use Studio's console or the CLI to provision infrastructure; in Gen 2, you author TypeScript code in files following a file-based convention (such as `amplify/auth/resource.ts` or `amplify/auth/data.ts`). With TypeScript types and classes for resources, you gain strict typing and IntelliSense in Visual Studio Code to prevent errors. A breaking change in the backend code immediately reflects as a type error in the co-located frontend code. The file-based convention follows the "convention over configuration" paradigm—you know exactly where to look for resource definitions when you group them by type in separate files. +With the Gen 2 DX, you can provision backend infrastructure by authoring TypeScript. In the following diagram, the box at the bottom (outlined in pink), highlights the main difference in how you provision infrastructure compared to Gen 1. In Gen 1, you would use Studio's console or the CLI to provision infrastructure; in Gen 2, you author TypeScript code in files following a file-based convention (such as `amplify/auth/resource.ts` or `amplify/auth/data.ts`). With TypeScript types and classes for resources, you gain strict typing and IntelliSense in Visual Studio Code to prevent errors. A breaking change in the backend code immediately reflects as a type error in the co-located frontend code. The file-based convention follows the "convention over configuration" paradigm—you know exactly where to look for resource definitions when you group them by type in separate files. ![How Amplify capabilities can be used together or independently.](/images/gen2/how-amplify-works/amplify-flow.png) ### Faster local development -Per-developer cloud sandbox environments are optimized for faster iterations. Each developer on a team gets an isolated cloud development environment against which they can test their changes. These cloud sandbox environments are meant for local development only, but they deploy high-fidelity AWS backends while you build. Depending on the workflow, iterative updates are now deployed up to 8X faster than Gen-1 deployments. In the diagram below, four developers are able to work on fullstack features independently without disrupting each other's environments. +Per-developer cloud sandbox environments are optimized for faster iterations. Each developer on a team gets an isolated cloud development environment against which they can test their changes. These cloud sandbox environments are meant for local development only, but they deploy high-fidelity AWS backends while you build. Depending on the workflow, iterative updates are now deployed up to 8X faster than Gen 1 deployments. In the diagram below, four developers are able to work on fullstack features independently without disrupting each other's environments. ![How cloud sandbox environments work.](/images/gen2/how-amplify-works/sandbox.png) ### Fullstack Git-based environments -With Gen 2, all shared environments (such as `production`, `staging`, `gamma`) map 1:1 to Git branches in your repository. New features can be tested in ephemeral environments with pull request previews (or feature branches) before they are merged into production. Unlike the Gen-1 experience, which requires users to configure a number of steps in the CLI or Console to set up a fullstack environment, the Gen-2 experience is zero-config. Because of our code-first approach, the Git repository is always the source of truth for the state of the fullstack app—all backend resources are defined as code for reproducibility and portability across branches. This, along with central management of environment variables and secrets, simplifies the promotion workflow from lower to upper environments. +With Gen 2, all shared environments (such as `production`, `staging`, `gamma`) map 1:1 to Git branches in your repository. New features can be tested in ephemeral environments with pull request previews (or feature branches) before they are merged into production. Unlike the Gen 1 experience, which requires users to configure a number of steps in the CLI or Console to set up a fullstack environment, the Gen 2 experience is zero-config. Because of our code-first approach, the Git repository is always the source of truth for the state of the fullstack app—all backend resources are defined as code for reproducibility and portability across branches. This, along with central management of environment variables and secrets, simplifies the promotion workflow from lower to upper environments. ![How fullstack deployments work.](/images/gen2/how-amplify-works/fullstack.png) ### Unified management console -All branches can be managed in the new Amplify console. The Gen-2 Amplify console consolidates the console experiences across Studio and Hosting, providing a single place for you to manage your builds, hosting settings (such as custom domains), deployed resources (such as data browser or user management), and environment variables and secrets. Even though you can access deployed resources directly in other AWS service consoles, the Amplify console will offer a first-party experience for the categories almost every app needs—data, auth, storage, and functions. For example, with Data, Amplify offers an API playground and a data manager (coming soon) with relationship building, seed data generation, and file upload capabilities. +All branches can be managed in the new Amplify console. The Gen 2 Amplify console consolidates the console experiences across Studio and Hosting, providing a single place for you to manage your builds, hosting settings (such as custom domains), deployed resources (such as data browser or user management), and environment variables and secrets. Even though you can access deployed resources directly in other AWS service consoles, the Amplify console will offer a first-party experience for the categories almost every app needs—data, auth, storage, and functions. For example, with Data, Amplify offers an API playground and a data manager (coming soon) with relationship building, seed data generation, and file upload capabilities.