diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4bc3e60d1b8..e1ea17fb3d4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -104,6 +104,7 @@ /src/themes @aws-amplify/documentation-team /src/utils @aws-amplify/documentation-team /tasks @aws-amplify/documentation-team +.github @aws-amplify/documentation-team #Protected Content /src/protected @reesscot @srquinn21 @Milan-Shah @swaminator diff --git a/.github/workflows/check_for_console_errors.yml b/.github/workflows/check_for_console_errors.yml new file mode 100644 index 00000000000..e3852ba97a0 --- /dev/null +++ b/.github/workflows/check_for_console_errors.yml @@ -0,0 +1,38 @@ +name: CheckConsoleErrors +on: + pull_request: + branches: [main] + types: [opened, synchronize] +permissions: + contents: read +jobs: + CheckConsoleErrors: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.2 https://github.com/actions/checkout/commit/b4ffde65f46336ab88eb53be808477a3936bae11 + - name: Setup Node.js 20.x + uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0 https://github.com/actions/setup-node/commit/e33196f7422957bea03ed53f6fbb155025ffc7b8 + with: + node-version: 20.x + - name: Install Dependencies + run: yarn + - name: Run Build + run: yarn build:release + env: + NODE_OPTIONS: --max_old_space_size=4096 + - name: Run Server + run: | + python -m http.server 3000 -d ${{ vars.BUILD_DIR }} & + sleep 5 + - name: Run Console Errors + id: consoleErrors + uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1 https://github.com/actions/github-script/commit/d7906e4ad0b1822421a7e6a35d5ca353c962f410 + with: + result-encoding: string + script: | + const { consoleErrors } = require('./tasks/console-errors.js'); + return await consoleErrors(); + - name: Fail if console errors have been found + if: ${{ steps.consoleErrors.outputs.result }} + run: exit 1 diff --git a/cspell.json b/cspell.json index b97dae7ded7..69817bdd7eb 100644 --- a/cspell.json +++ b/cspell.json @@ -1520,7 +1520,7 @@ "privatesaccess", "menudetaileditors", "editorgroupaccess", - "publicauthreadonly", + "authreadonly", "envs", "Onetoone", "onetomany", diff --git a/package.json b/package.json index 73ee7d22885..7cb97565ea8 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,8 @@ "@adobe/css-tools": "4.3.2", "follow-redirects": "^1.15.6", "ip": "2.0.1", - "sharp": "0.32.6" + "sharp": "0.32.6", + "ejs": "3.1.10" }, "scripts": { "clean": "rm -rf node_modules yarn.lock", diff --git a/public/images/console/7_publicauthreadonly.png b/public/images/console/7_authreadonly.png similarity index 100% rename from public/images/console/7_publicauthreadonly.png rename to public/images/console/7_authreadonly.png diff --git a/public/images/gen2/getting-started/data-manager.png b/public/images/gen2/getting-started/data-manager.png new file mode 100644 index 00000000000..94fa632e41c Binary files /dev/null and b/public/images/gen2/getting-started/data-manager.png differ diff --git a/src/components/BlockSwitcher/__tests__/BlockSwitcher.test.tsx b/src/components/BlockSwitcher/__tests__/BlockSwitcher.test.tsx index 7c9989f7d91..eae43ec719c 100644 --- a/src/components/BlockSwitcher/__tests__/BlockSwitcher.test.tsx +++ b/src/components/BlockSwitcher/__tests__/BlockSwitcher.test.tsx @@ -21,12 +21,12 @@ describe('BlockSwitcher', () => { const blockSwitcher = await screen.findByText(blockAContent); expect(blockSwitcher).toBeInTheDocument(); }); - + it('should have more than one Block', async () => { render(component); expect(component.props.children.length).toBeGreaterThan(1); }); - + it('should show the first Block as default', async () => { render(component); const tabs = await screen.getAllByRole('tab'); @@ -39,7 +39,7 @@ describe('BlockSwitcher', () => { expect(panels[1]).not.toHaveClass('amplify-tabs__panel--active'); expect(panels[2]).not.toHaveClass('amplify-tabs__panel--active'); }); - + it('should load all Blocks to the DOM', async () => { render(component); const blockA = await screen.findByText(blockAContent); @@ -49,7 +49,7 @@ describe('BlockSwitcher', () => { expect(blockB).toBeInTheDocument(); expect(blockC).toBeInTheDocument(); }); - + it('should switch tabs upon click', async () => { render(component); const tabs = await screen.getAllByRole('tab'); diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index 722c7c1c300..ab55076eb7e 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -144,6 +144,9 @@ export const directory = { { path: 'src/pages/[platform]/build-a-backend/auth/multi-step-sign-in/index.mdx' }, + { + path: 'src/pages/[platform]/build-a-backend/auth/sign-in-with-web-ui/index.mdx' + }, { path: 'src/pages/[platform]/build-a-backend/auth/app-uninstall/index.mdx' }, @@ -158,6 +161,9 @@ export const directory = { }, { path: 'src/pages/[platform]/build-a-backend/auth/moving-to-production/index.mdx' + }, + { + path: 'src/pages/[platform]/build-a-backend/auth/advanced-workflows/index.mdx' } ] }, diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/existing-resources/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/existing-resources/index.mdx index 9bd6f377cc9..2d38978a8c1 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/existing-resources/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/existing-resources/index.mdx @@ -114,7 +114,7 @@ Note that before you can add an AWS resource to your application, the applicatio -Existing Amazon Pinpoint resources can be used with the Amplify Libraries by referencing your **Application ID** and **Region** in your `amplifyconfiguration.json` file. +Existing Amazon Pinpoint resources can be used with the Amplify Libraries by referencing your **Application ID** and **Region** in your `amplify_outputs.json` file. ```json { @@ -139,7 +139,7 @@ Note that before you can add an AWS resource to your application, the applicatio -Existing Amazon Pinpoint resources can be used with the Amplify Libraries by referencing your **Application ID** and **Region** in your `amplifyconfiguration.json` file. +Existing Amazon Pinpoint resources can be used with the Amplify Libraries by referencing your **Application ID** and **Region** in your `amplify_outputs.json` file. ```json { diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/record-events/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/record-events/index.mdx index 6986ee9884b..770cf8e3e5c 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/record-events/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/record-events/index.mdx @@ -92,7 +92,7 @@ However, it can take upwards of 30 minutes for the event to display in the Filte ## Flush events -Events have a default configuration to flush out to the network every 30 seconds. If you would like to change this, update `amplifyconfiguration.json` with the value in milliseconds you would like for `autoFlushEventsInterval`. This configuration will flush events every 10 seconds: +Events have a default configuration to flush out to the network every 30 seconds. If you would like to change this, update `amplify_outputs.json` with the value in milliseconds you would like for `autoFlushEventsInterval`. This configuration will flush events every 10 seconds: ```json { @@ -157,7 +157,7 @@ You can report authentication events by doing either of the following: - Managing user sign-up and sign-in with Amazon Cognito user pools. - Cognito user pools are user directories that make it easier to add sign-up and sign-in to your app. As users authenticate with your app, Cognito reports authentication events to Pinpoint. For more information, see [Using Amazon Pinpoint Analytics with Amazon Cognito User Pools](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-pinpoint-integration.html) in the _Amazon Cognito Developer Guide_. Also update **amplifyconfiguration.json** by adding the `PinpointAppId` key under `CognitoUserPool`. + Cognito user pools are user directories that make it easier to add sign-up and sign-in to your app. As users authenticate with your app, Cognito reports authentication events to Pinpoint. For more information, see [Using Amazon Pinpoint Analytics with Amazon Cognito User Pools](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-pinpoint-integration.html) in the _Amazon Cognito Developer Guide_. Also update **amplify_outputs.json** by adding the `PinpointAppId` key under `CognitoUserPool`. ```json "CognitoUserPool": { @@ -416,7 +416,7 @@ However, it can take upwards of 30 minutes for the event to display in the Filte By default, events are automatically flushed out to the network every 60 seconds. -If you would like to change this, update `amplifyconfiguration.json` and set the value you would prefer under `autoFlushEventsInterval`, expressed in seconds: +If you would like to change this, update `amplify_outputs.json` and set the value you would prefer under `autoFlushEventsInterval`, expressed in seconds: ```json { @@ -463,7 +463,7 @@ You can report authentication events by doing either of the following: - Managing user sign-up and sign-in with Amazon Cognito user pools. - Cognito user pools are user directories that make it easier to add sign-up and sign-in to your app. As users authenticate with your app, Cognito reports authentication events to Pinpoint. For more information, see [Using Amazon Pinpoint Analytics with Amazon Cognito User Pools](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-pinpoint-integration.html) in the _Amazon Cognito Developer Guide_. Also update **amplifyconfiguration.json** by adding the `PinpointAppId` key under `CognitoUserPool`. + Cognito user pools are user directories that make it easier to add sign-up and sign-in to your app. As users authenticate with your app, Cognito reports authentication events to Pinpoint. For more information, see [Using Amazon Pinpoint Analytics with Amazon Cognito User Pools](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-pinpoint-integration.html) in the _Amazon Cognito Developer Guide_. Also update **amplify_outputs.json** by adding the `PinpointAppId` key under `CognitoUserPool`. ```json "CognitoUserPool": { diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx index a6cbef7618b..f4ff0a66bae 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx @@ -43,7 +43,7 @@ An application with Amplify libraries integrated and a minimum target of any of -visionOS support is currently in **preview** and can be used by targeting the [`visionos-preview`](https://github.com/aws-amplify/amplify-swift/tree/visionos-preview) branch. +visionOS support is currently in **preview** and can be used by targeting the [`visionos-preview`](https://github.com/aws-amplify/amplify-swift/tree/visionos-preview) branch. As new Xcode 15 beta versions are released, the branch will be updated with any necessary fixes on a best effort basis. For more information on how to use the `visionos-preview` branch, see [Platform Support](https://github.com/aws-amplify/amplify-swift/tree/visionos-preview#platform-support). @@ -75,7 +75,7 @@ For more information on how to use the `visionos-preview` branch, see [Platform ## Set up Analytics backend -Use the [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/home.html) to create an analytics resource powered by [Amazon Pinpoint](https://aws.amazon.com/pinpoint/). +Use the [AWS CDK](https://docs.aws.amazon.com/cdk/latest/guide/home.html) to create an analytics resource powered by [Amazon Pinpoint](https://aws.amazon.com/pinpoint/). ```ts title="amplify/backend.ts" import { auth } from "./auth/resource"; @@ -85,9 +85,9 @@ import { CfnApp } from "aws-cdk-lib/aws-pinpoint"; import { Stack } from "aws-cdk-lib/core"; const backend = defineBackend({ - auth, + auth, data, - // additional resources + // additional resources }); const analyticsStack = backend.createStack("analytics-stack"); @@ -186,7 +186,7 @@ Import and load the configuration file in your app. It's recommended you add the ```js title="src/index.js" import { Amplify } from 'aws-amplify'; import { record } from 'aws-amplify/analytics'; -import amplifyconfig from '../amplifyconfiguration.json'; +import outputs from '../amplify_outputs.json'; Amplify.configure({ ...Amplify.getConfig(), @@ -195,11 +195,11 @@ Amplify.configure({ ``` - + ```js title="pages/_app.tsx" import { Amplify } from 'aws-amplify'; import { record } from 'aws-amplify/analytics'; -import amplifyconfig from '@/amplifyconfiguration.json'; +import outputs from '@/amplify_outputs.json'; Amplify.configure({ ...Amplify.getConfig(), @@ -230,7 +230,7 @@ init() { do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) try Amplify.add(plugin: AWSPinpointAnalyticsPlugin()) - try Amplify.configure() + try Amplify.configure(with: .amplifyOutputs) print("Amplify configured with Auth and Analytics plugins") } catch { print("Failed to initialize Amplify with \(error)") @@ -260,7 +260,7 @@ func application( do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) try Amplify.add(plugin: AWSPinpointAnalyticsPlugin()) - try Amplify.configure() + try Amplify.configure(with: .amplifyOutputs) print("Amplify configured with Auth and Analytics plugins") } catch { print("Failed to initialize Amplify with \(error)") @@ -323,7 +323,7 @@ Future _configureAmplify() async { // Once Plugins are added, configure Amplify // Note: Amplify can only be configured once. try { - await Amplify.configure(amplifyconfig); + await Amplify.configure(outputs); } on AmplifyAlreadyConfiguredException { safePrint( 'Tried to reconfigure Amplify; this can occur when your app restarts on Android.', @@ -351,7 +351,7 @@ Future _configureAmplify() async { // Once Plugins are added, configure Amplify // Note: Amplify can only be configured once. try { - await Amplify.configure(amplifyconfig); + await Amplify.configure(outputs); } on AmplifyAlreadyConfiguredException { safePrint( 'Tried to reconfigure Amplify; this can occur when your app restarts on Android.', @@ -367,7 +367,7 @@ Future main() async { class MyApp extends StatefulWidget { const MyApp({Key? key}): super(key: key); - + // ... } ``` @@ -378,6 +378,16 @@ To initialize the Amplify Auth and Analytics categories you call `Amplify.addPlu Add the following code to your `onCreate()` method in your application class: + +Before calling the `Amplify.configure` function, make sure to either download the `amplify_outputs.json` file from the console, or generate it with the following command: + +```bash title="Terminal" showLineNumbers={false} +npx ampx generate outputs --app-id --branch main --out-dir app/src/main/res/raw +``` + +Next, be sure the file you generated or downloaded is in the appropriate resource directory for your application (for example, `app/src/main/res/raw`) in your Android project. Otherwise, you will not be able to compile your application. + + @@ -387,6 +397,8 @@ import com.amplifyframework.AmplifyException; import com.amplifyframework.analytics.pinpoint.AWSPinpointAnalyticsPlugin; import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin; import com.amplifyframework.core.Amplify; +import com.amplifyframework.core.configuration.AmplifyOutputs; + ``` ```java @@ -406,7 +418,7 @@ public class MyAmplifyApp extends Application { // Add these lines to add the AWSCognitoAuthPlugin and AWSPinpointAnalyticsPlugin plugins Amplify.addPlugin(new AWSCognitoAuthPlugin()); Amplify.addPlugin(new AWSPinpointAnalyticsPlugin()); - Amplify.configure(getApplicationContext()); + Amplify.configure(AmplifyOutputs.fromResource(R.raw.amplify_outputs), getApplicationContext()); Log.i("MyAmplifyApp", "Initialized Amplify"); } catch (AmplifyException error) { @@ -425,7 +437,8 @@ import com.amplifyframework.AmplifyException import com.amplifyframework.analytics.pinpoint.AWSPinpointAnalyticsPlugin import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin import com.amplifyframework.core.Amplify -``` +import com.amplifyframework.core.configuration.AmplifyOutputs +``` ```kotlin Amplify.addPlugin(AWSCognitoAuthPlugin()) @@ -443,7 +456,7 @@ class MyAmplifyApp : Application() { // Add these lines to add the AWSCognitoAuthPlugin and AWSPinpointAnalyticsPlugin plugins Amplify.addPlugin(AWSCognitoAuthPlugin()) Amplify.addPlugin(AWSPinpointAnalyticsPlugin()) - Amplify.configure(applicationContext) + Amplify.configure(AmplifyOutputs.fromResource(R.raw.amplify_outputs), applicationContext) Log.i("MyAmplifyApp", "Initialized Amplify") } catch (error: AmplifyException) { @@ -461,6 +474,7 @@ import android.util.Log; import com.amplifyframework.AmplifyException; import com.amplifyframework.analytics.pinpoint.AWSPinpointAnalyticsPlugin; import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin; +import com.amplifyframework.core.configuration.AmplifyOutputs; import com.amplifyframework.rx.RxAmplify; ``` @@ -481,7 +495,7 @@ public class MyAmplifyApp extends Application { // Add these lines to add the AWSCognitoAuthPlugin and AWSPinpointAnalyticsPlugin plugins RxAmplify.addPlugin(new AWSCognitoAuthPlugin()); RxAmplify.addPlugin(new AWSPinpointAnalyticsPlugin()); - RxAmplify.configure(getApplicationContext()); + RxAmplify.configure(AmplifyOutputs.fromResource(R.raw.amplify_outputs), getApplicationContext()); Log.i("MyAmplifyApp", "Initialized Amplify"); } catch (AmplifyException error) { diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/geo/amazon-location-sdk/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/geo/amazon-location-sdk/index.mdx index 0edc958c4e2..2102dae200c 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/geo/amazon-location-sdk/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/geo/amazon-location-sdk/index.mdx @@ -63,8 +63,8 @@ import { LocationClient, AssociateTrackerConsumerCommand } from '@aws-sdk/client-location'; -import amplifyconfig from '../amplifyconfiguration.json'; -Amplify.configure(amplifyconfig); +import outputs from '../amplify_outputs.json'; +Amplify.configure(outputs); const createClient = async () => { const session = await fetchAuthSession(); diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/geo/configure-geofencing/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/geo/configure-geofencing/index.mdx index 14a90b78983..fd7c4220bf3 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/geo/configure-geofencing/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/geo/configure-geofencing/index.mdx @@ -4,12 +4,10 @@ export const meta = { title: 'Configure a geofence collection', description: 'Create and manage collections of Geofences', platforms: [ - 'android', 'angular', 'javascript', 'nextjs', 'react', - 'swift', 'vue' ] }; @@ -100,7 +98,7 @@ backend.addOutput({ ## Geofence Collection Pricing Plan -The pricing plan for the Geofence Collection will be set to `RequestBasedUsage`. We advice you to go through the [location service pricing](https://aws.amazon.com/location/pricing/) along with the [location service terms](https://aws.amazon.com/service-terms/) (_82.5 section_) to learn more about the pricing plan. +The pricing plan for the Geofence Collection will be set to `RequestBasedUsage`. We advice you to go through the [location service pricing](https://aws.amazon.com/location/pricing/) along with the [location service terms](https://aws.amazon.com/service-terms/) (_82.5 section_) to learn more about the pricing plan. #### Group access diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/geo/existing-resources/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/geo/existing-resources/index.mdx index 069b8b3ef5e..e2abf809405 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/geo/existing-resources/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/geo/existing-resources/index.mdx @@ -80,13 +80,13 @@ There are two roles created by Cognito: an `Auth_Role` that grants signed-in-use ## In your application -You can first import and configure the generated `amplifyconfiguration.json`. You can then manually configure Amplify Geo like this: +You can first import and configure the generated `amplify_outputs.json`. You can then manually configure Amplify Geo like this: ```js import { Amplify } from 'aws-amplify'; -import amplifyconfig from '../amplifyconfiguration.json'; +import outputs from '../amplify_outputs.json'; -Amplify.configure(amplifyconfig); +Amplify.configure(outputs); Amplify.configure({ ...Amplify.getConfig(), Geo: { @@ -121,7 +121,7 @@ Now you can proceed to [displaying a map](/[platform]/build-a-backend/add-aws-se ## In your app configuration -Amplify Geo is dependent on your Amplify Auth category. If it is not already configured, then you will have to manually configure it as well. In order to manually configure Amplify Geo category, you must edit `amplifyconfiguration.json` in your project's `src/main/res/raw` directory with your information from existing Amazon Cognito and Amazon Location Service resources. +Amplify Geo is dependent on your Amplify Auth category. If it is not already configured, then you will have to manually configure it as well. In order to manually configure Amplify Geo category, you must edit `amplify_outputs.json` in your project's `src/main/res/raw` directory with your information from existing Amazon Cognito and Amazon Location Service resources. ```json { @@ -177,7 +177,7 @@ Now you can proceed to [displaying a map](/[platform]/build-a-backend/add-aws-se Amplify Geo is dependent on your Amplify Auth category. If it is not already configured, then you will need to [configure it](/[platform]/build-a-backend/auth/set-up-auth/) as well. -Existing Amazon Location Service resources can be used with the Amplify Libraries by adding information about the resources to your `amplifyconfiguration.json` file. +Existing Amazon Location Service resources can be used with the Amplify Libraries by adding information about the resources to your `amplify_outputs.json` file. ```json { diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/geo/geofences/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/geo/geofences/index.mdx index d0ed9ee4de3..0a170a90b08 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/geo/geofences/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/geo/geofences/index.mdx @@ -118,8 +118,8 @@ import { AmplifyGeofenceControl } from 'maplibre-gl-js-amplify'; import '@aws-amplify/ui-react/styles.css'; import '@aws-amplify/ui-react-geo/styles.css'; import 'maplibre-gl-js-amplify/dist/public/amplify-ctrl-geofence.css'; -import amplifyconfig from '../amplifyconfiguration.json'; -Amplify.configure(amplifyconfig); +import outputs from '../amplify_outputs.json'; +Amplify.configure(outputs); function Geofence() { useControl(() => new AmplifyGeofenceControl()); @@ -181,7 +181,7 @@ Geo.saveGeofences(geofences, options) => Promise; - `geofences` - can be a single geofence object, or an array of geofence objects to save to a collection. - `options` - optional options object for saving geofences - `collectionName` - the name of the collection to save geofences to. - - Defaults to the default collection listed in your `amplifyconfiguration.json` file after provisioning a geofence collection resource. + - Defaults to the default collection listed in your `amplify_outputs.json` file after provisioning a geofence collection resource. Geofence objects must have the following properties: @@ -260,7 +260,7 @@ Geo.getGeofence(geofenceId, options) => Promise; - `geofenceId` - the `id` of the geofence to get. - `options` - optional options object for getting a geofence - `collectionName` - the name of the collection to get geofence from. - - Defaults to the default collection listed in your `amplifyconfiguration.json` file after provisioning a geofence collection resource. + - Defaults to the default collection listed in your `amplify_outputs.json` file after provisioning a geofence collection resource. #### Return @@ -293,7 +293,7 @@ Geo.listGeofences(options) => Promise; - `nextToken` - the pagination token for the next page of geofences. - if no token is given, it will return the first page of geofences. - `collectionName` - the name of the collection to save geofences to. - - Defaults to the default collection listed in your `amplifyconfiguration.json` file after provisioning a geofence collection resource. + - Defaults to the default collection listed in your `amplify_outputs.json` file after provisioning a geofence collection resource. #### Return @@ -329,7 +329,7 @@ Geo.deleteGeofences(geofenceIds, options) => Promise; - `geofenceIds` - a single geofenceId or array of geofenceIds to delete - `options` - optional options object for saving geofences - `collectionName` - the name of the collection to save geofences to. - - Defaults to the default collection listed in your `amplifyconfiguration.json` file after provisioning a geofence collection resource. + - Defaults to the default collection listed in your `amplify_outputs.json` file after provisioning a geofence collection resource. #### Return diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/geo/google-migration/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/geo/google-migration/index.mdx index 06cfcbe41c1..ed666d9d7ab 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/geo/google-migration/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/geo/google-migration/index.mdx @@ -46,7 +46,7 @@ A key difference to notice between using Amplify Geo and Google Maps is with Goo ### Authorization -When using Google Maps Platform or other similar services like Mapbox you will first be prompted to go to the Google Cloud Console to set up APIs and create an API key where you will then use the API key when requesting the Google Maps JS API. With Amplify Geo you will instead setup Amplify Auth and the `MapView` component will read the auth configuration from the `amplifyconfiguration.json` file. Behind the scenes Amplify Auth uses Amazon Cognito to set up client credentials with access to Location Service and Geo will use those credentials when making any location related API calls. More information on setting Amplify Auth and Geo can be found below in the `Setting Up Amplify` section. +When using Google Maps Platform or other similar services like Mapbox you will first be prompted to go to the Google Cloud Console to set up APIs and create an API key where you will then use the API key when requesting the Google Maps JS API. With Amplify Geo you will instead setup Amplify Auth and the `MapView` component will read the auth configuration from the `amplify_outputs.json` file. Behind the scenes Amplify Auth uses Amazon Cognito to set up client credentials with access to Location Service and Geo will use those credentials when making any location related API calls. More information on setting Amplify Auth and Geo can be found below in the `Setting Up Amplify` section. ## Create a webpage @@ -102,10 +102,10 @@ When using Google Maps Platform or other similar services like Mapbox you will f
@@ -119,7 +119,7 @@ This code imports the MapLibre GL JS library and CSS, one of the popular options ### Setting up Amplify 1. You will need to setup a [Geo Map resources](/[platform]/build-a-backend/add-aws-services/geo/set-up-geo/). Follow instructions for creating a map. -1. Once the workflow has completed you should have an `amplifyconfiguration.json` file in the same directory as your `index.html` file. +1. Once the workflow has completed you should have an `amplify_outputs.json` file in the same directory as your `index.html` file. 1. Save your `index.html` file. ## Display a map diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/geo/maps/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/geo/maps/index.mdx index 55b520bdb49..5c27c99058e 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/geo/maps/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/geo/maps/index.mdx @@ -243,14 +243,14 @@ To display a map on your html website, add the following scripts to your html we ``` -Next, add a div element with id `map` anywhere in your webpage where you want to render the map. Include the following code snippet to configure Amplify (update the `amplifyconfiguration.json` file path accordingly) and instantiate the map. +Next, add a div element with id `map` anywhere in your webpage where you want to render the map. Include the following code snippet to configure Amplify (update the `amplify_outputs.json` file path accordingly) and instantiate the map. ```html + + +``` + +Add the following to `style.css` file: + +{/* cSpell:disable */} + +```css title="style.css" +body { + margin: 0; + background: linear-gradient(180deg, rgb(117, 81, 194), rgb(255, 255, 255)); + display: flex; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + height: 100vh; + width: 100vw; + justify-content: center; + align-items: center; +} + +main { + display: flex; + flex-direction: column; + align-items: stretch; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; + color: white; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +ul { + padding-inline-start: 0; + margin-block-start: 0; + margin-block-end: 0; + list-style-type: none; + display: flex; + flex-direction: column; + margin: 8px 0; + border: 1px solid black; + gap: 1px; + background-color: black; + border-radius: 8px; + overflow: auto; +} + +li { + background-color: white; + padding: 8px; +} + +li:hover { + background: #dadbf9; +} + +a { + font-weight: 800; + text-decoration: none; +} +``` +{/* cSpell:enable */} + +In `main.js` remove the boilerplate code and leave it empty. Then refresh the browser to see the changes. + +## Create Backend + +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 title="Terminal" showLineNumbers={false} +npm create amplify@latest +? Where should we create your project? (.) # press enter +``` + +Running this command will scaffold Amplify backend files in your current project with the following files added: + +```text +├── amplify/ +│ ├── auth/ +│ │ └── resource.ts +│ ├── data/ +│ │ └── resource.ts +│ ├── backend.ts +│ └── package.json +├── node_modules/ +├── index.html +├── style.css +├── .gitignore +├── package-lock.json +├── package.json +└── tsconfig.json +``` + +### Set up local AWS credentials + +To make backend updates, we are going to require AWS credentials to deploy backend updates from our local machine. + +**Skip ahead to step 8**, if you already have an AWS profile with credentials on your local machine, and your AWS profile has the `AmplifyBackendDeployFullAccess` permission policy. + +Otherwise, **[set up local AWS credentials](/[platform]/start/account-setup/)** that grant Amplify permissions to deploy backend updates from your local machine. + +### Deploy cloud sandbox + +To deploy your backend use Amplify's per-developer cloud sandbox. This feature provides a separate backend environment for every developer on a team, ideal for local development and testing. To run your application with a sandbox environment, you can run the following command: + +```bash title="Terminal" showLineNumbers={false} +npx ampx sandbox +``` + +Once the sandbox environment is deployed, it will create a GraphQL API, database, and auth service. All the deployed resources will be available in the `amplify_outputs.json`. However, Xcode won't be able to recognize them. For recognizing the files, you need to drag and drop the generated files to your project. + +## Connect frontend to backend + +The initial scaffolding already has a pre-configured data backend defined in the `amplify/data/resource.ts` file. The default example will create a Todo model with `content` field. Update your main.js file to create new to-do items. + +```typescript title="src/main.ts" +import { generateClient } from "aws-amplify/data"; +import type { Schema } from "../amplify/data/resource"; +import './style.css'; +import { Amplify } from 'aws-amplify'; +import outputs from '../amplify_outputs.json'; + +Amplify.configure(outputs); + + +const client = generateClient(); + +document.addEventListener("DOMContentLoaded", function () { + const todos: Array = []; + const todoList = document.getElementById("todoList") as HTMLUListElement; + const addTodoButton = document.getElementById("addTodo") as HTMLButtonElement; + + addTodoButton.addEventListener("click", createTodo); + + function updateUI() { + todoList.innerHTML = ''; + todos.forEach(todo => { + const li = document.createElement('li'); + li.textContent = todo.content ?? ''; + todoList.appendChild(li); + }); + } + + function createTodo() { + console.log('createTodo'); + const content = window.prompt("Todo content"); + if (content) { + client.models.Todo.create({ content }).then(response => { + if (response.data && !response.errors) { + todos.push(response.data); + updateUI(); + } else { + console.error('Error creating todo:', response.errors); + alert('Failed to create todo.'); + } + }).catch(error => { + console.error('Network or other error:', error); + alert('Failed to create todo due to a network or other error.'); + }); + } + } + + + client.models.Todo.observeQuery().subscribe({ + next: (data) => { + todos.splice(0, todos.length, ...data.items); + updateUI(); + } + }); +}); +``` + +
@@ -112,7 +352,7 @@ Let's take a tour of the project structure in this starter repository by opening In the Amplify Console, navigate to the Data manager to see the data entered in your database. -TK IMAGE +![Amplify data manager user interface showing the ToDo model with two to do instances](/images/gen2/getting-started/data-manager.png) ## Make frontend updates @@ -449,7 +689,7 @@ Let's take a tour of the project structure in this starter repository by opening In the Amplify Console, navigate to the Data manager to see the data entered in your database. -TK IMAGE +![Amplify data manager user interface showing the ToDo model with two to do instances](/images/gen2/getting-started/data-manager.png) ## Make frontend updates @@ -782,7 +1022,7 @@ Let's take a tour of the project structure in this starter repository by opening In the Amplify Console, navigate to the Data manager to see the data entered in your database. -TK IMAGE +![Amplify data manager user interface showing the ToDo model with two to do instances](/images/gen2/getting-started/data-manager.png) ## Make frontend updates @@ -880,11 +1120,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { TodosComponent } from './todos/todos.component'; import { Amplify } from 'aws-amplify'; -import config from '../../amplifyconfiguration.json'; +import outputs from '../../amplify_outputs.json'; // highlight-next-line import { AmplifyAuthenticatorModule, AuthenticatorService } from '@aws-amplify/ui-angular'; -Amplify.configure(config); +Amplify.configure(outputs); @Component({ selector: 'app-root', @@ -898,7 +1138,7 @@ export class AppComponent { title = 'amplify-angular-template'; // highlight-start constructor(public authenticator: AuthenticatorService) { - Amplify.configure(config); + Amplify.configure(outputs); } // highlight-end } @@ -1074,7 +1314,7 @@ flutter create my_amplify_app 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 title="Terminal" showLineNumbers={false} -npm create amplify@beta +npm create amplify@latest ? Where should we create your project? (.) # press enter ``` @@ -1152,7 +1392,7 @@ Future main() async { Future _configureAmplify() async { try { await Amplify.addPlugin(AmplifyAuthCognito()); - await Amplify.configure(amplifyConfig); + await Amplify.configure(outputs); safePrint('Successfully configured'); } on Exception catch (e) { safePrint('Error configuring Amplify: $e'); @@ -1250,7 +1490,7 @@ Future _configureAmplify() async { AmplifyAPI(modelProvider: ModelProvider.instance), ], ); - await Amplify.configure(amplifyConfig); + await Amplify.configure(outputs); safePrint('Successfully configured'); } on Exception catch (e) { safePrint('Error configuring Amplify: $e'); @@ -1572,42 +1812,34 @@ struct MyApp: App { var body: some Scene { WindowGroup { - Authenticator { state in - VStack { - Button("Sign out") { - Task { - await state.signOut() - } - } - Spacer() - Button(action: { - Task { - await createTodo() - await listTodos() - } - }) { - HStack { - Text("Add a New Todo") - Image(systemName: "plus") - } - } - .accessibilityLabel("New Todo") - } - } + ContentView() } } +} +``` - func createTodo() async { - } +Update `ContentView` with the following code: +```swift +import Amplify +import Authenticator - func listTodo() async { +struct ContentView: View { + var body: some View { + Authenticator { state in + VStack { + Button("Sign out") { + Task { + await state.signOut() + } + } + } + } } } ``` The Authenticator component auto-detects your auth backend settings and renders the correct UI state based on the auth backend's authentication flow. - Run your application in your local environment again. You should be presented with a login experience now. - + ## 🥳 Success diff --git a/src/pages/[platform]/start/quickstart/nextjs-app-router-client-components/index.mdx b/src/pages/[platform]/start/quickstart/nextjs-app-router-client-components/index.mdx index 16da5ba3589..b2bc6b183b0 100644 --- a/src/pages/[platform]/start/quickstart/nextjs-app-router-client-components/index.mdx +++ b/src/pages/[platform]/start/quickstart/nextjs-app-router-client-components/index.mdx @@ -125,7 +125,7 @@ git clone https://github.com//amplify-next-template.git cd amplify-next-template && npm install ``` -Now move the `amplifyconfiguration.json` file you downloaded above to the root of your project. +Now move the `amplify_outputs.json` file you downloaded above to the root of your project. ```text ├── amplify diff --git a/src/pages/[platform]/start/quickstart/nextjs-pages-router/index.mdx b/src/pages/[platform]/start/quickstart/nextjs-pages-router/index.mdx index 2d8aa237046..5b15d2aa601 100644 --- a/src/pages/[platform]/start/quickstart/nextjs-pages-router/index.mdx +++ b/src/pages/[platform]/start/quickstart/nextjs-pages-router/index.mdx @@ -126,7 +126,7 @@ git clone https://github.com//amplify-next-template.git cd amplify-next-template && npm install ``` -Now move the `amplifyconfiguration.json` file you downloaded above to the root of your project. +Now move the `amplify_outputs.json` file you downloaded above to the root of your project. ```text ├── amplify diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx index e42414f412d..92d95900ab0 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx @@ -44,7 +44,7 @@ import common_configureaccess from '/src/fragments/lib/storage/native_common/con We recommend using the new [Amplify Gen2](https://docs.amplify.aws/gen2/build-a-backend/storage/) experience when defining file access permissions as it offers a more flexible approach to customize access to files. -Note: `accessLevel` parameter is deprecated and maybe removed in next major version. +Note: `accessLevel` parameter is deprecated and may be removed in the next major version. Storage module can manage files with three different access levels; `guest`, `protected` and `private`. The Amplify CLI configures three different access levels on the storage bucket: guest, protected and private. When you run `amplify add storage`, the CLI will configure appropriate IAM policies on the bucket using a Cognito identity pool Role. You will have the option of adding CRUD (Create/Update, Read and Delete) based permissions as well, so that Authenticated and Guest users will be granted limited permissions within these levels. diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/list/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/list/index.mdx index 8039f3658d6..737f01bec5e 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/list/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/list/index.mdx @@ -250,4 +250,8 @@ const loadNextPage = async () => { Note: The range of pageSize can be from 0 - 1000. +## List with no prefix (Deprecated) + +The usage of the `list` API without a specified prefix `await list()` or with an empty string as the prefix `await list({ prefix: "" })` is now deprecated and may be removed in the next major version. + diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/path/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/path/index.mdx index 23f9c4627a8..e99205a0874 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/path/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/path/index.mdx @@ -42,7 +42,8 @@ export function getStaticProps(context) { You can now use the `path` parameter in the API to access any path within your S3 bucket. This provides more flexibility than the predefined `protected`, `private`, or `guest` folder access levels, allowing you to create and manage a storage structure tailored to your needs. -Note : `path` parameter can not be empty or start with a '/' (leading slash) +Note: `path` parameter can not be empty or start with a '/' (leading slash) + The sections below explain how to use existing `guest`, `protected`, and `private` resources with path-based APIs. ## Using Guest accessLevel diff --git a/src/pages/gen1/[platform]/tools/console/authz/permissions/index.mdx b/src/pages/gen1/[platform]/tools/console/authz/permissions/index.mdx index 55ffd5832bf..28c1372be93 100644 --- a/src/pages/gen1/[platform]/tools/console/authz/permissions/index.mdx +++ b/src/pages/gen1/[platform]/tools/console/authz/permissions/index.mdx @@ -71,4 +71,4 @@ If you want your data model to be publicly accessible, switch to API_KEY or IAM 1. Using the *Books* data model that you created in the [Create a data model example](/gen1/[platform]/tools/console/data/data-model/#Create-a-data-model-example), set the authorization mode to **API Key**. 2. In the **Model** pane on the right, expand the **Anyone** window. Choose **Read** to specify that any signed in user has read access to the data in the *Book* model. -![](/images/console/7_publicauthreadonly.png) +![](/images/console/7_authreadonly.png) diff --git a/tasks/console-errors.js b/tasks/console-errors.js new file mode 100644 index 00000000000..cdc750fe460 --- /dev/null +++ b/tasks/console-errors.js @@ -0,0 +1,109 @@ +const puppeteer = require('puppeteer'); // eslint-disable-line +const { getSitemapUrls } = require('./get-sitemap-links'); // eslint-disable-line + +// Here we are excluding shortbread errors because these are domain specific and are expected to fail in a local environment +const excludedErrors = [ + { + type: 'Shortbread', + errorText: + "Shortbread failed to set user's cookie preference because the domain name that was passed" + } +]; + +const excludedScripts = [ + 'prod.assets.shortbread.aws', + 'prod.tools.shortbread.aws', + 'prod.tools.shortbread.aws.dev', + 'aa0.awsstatic.com', + 'alpha.d2c.marketing.aws.dev', + 'vs-alpha.aws.amazon.com' +]; + +const LOCALHOST = 'http://localhost:3000'; + +const checkPage = async (url) => { + const errorsFound = []; + const browser = await puppeteer.launch({ headless: 'new' }); + + const page = await browser.newPage(); + + await page.setRequestInterception(true); + + page + .on('pageerror', (message) => { + const errorText = message.message; + const excluded = excludedErrors.some((excludedError) => { + return errorText.includes(excludedError.errorText); + }); + + if (!excluded) { + errorsFound.push({ + page: url, + message: errorText + }); + } + }) + .on('console', (message) => { + if (message.type().toLowerCase() === 'error') { + const errorText = message.text(); + const callingScript = message.location().url; + const excludedFromError = excludedErrors.some((excludedError) => { + return errorText.includes(excludedError.errorText); + }); + const excludedFromScript = excludedScripts.some((excludedScript) => { + return callingScript.includes(excludedScript); + }); + const excluded = excludedFromError || excludedFromScript; + + if (!excluded) { + errorsFound.push({ + page: url, + message: errorText, + stackTrace: message.stackTrace() + }); + } + } + }) + + .on('request', (interceptedRequest) => { + const excludedFromScript = excludedScripts.some((excludedScript) => { + return interceptedRequest.url().includes(excludedScript); + }); + if (excludedFromScript) { + interceptedRequest.abort(); + } else interceptedRequest.continue(); + }); + + await page.goto(url, { waitUntil: 'domcontentloaded' }); + + await browser.close(); + + return errorsFound; +}; + +const consoleErrors = async (domain) => { + const pagesToCheck = await getSitemapUrls(domain); + let errorMessage = ''; + for (let i = 0; i < pagesToCheck.length; i++) { + const url = pagesToCheck[i]; + console.log(`checking page ${url}`); + const errorsFound = await checkPage(url); + errorsFound.forEach((error) => { + errorMessage += `${error.message} found on ${error.page}\n`; + }); + } + if (errorMessage != '') { + console.log( + `--------- Console errors have been found and need to be resolved in order to merge. ------- + ------- Please note that these errors could be on pages that were not edited in this PR. ---------` + ); + console.log(errorMessage); + } + return errorMessage; +}; + +module.exports = { + consoleErrors: async (domain = LOCALHOST) => { + return await consoleErrors(domain); + } +}; diff --git a/tasks/get-sitemap-links.js b/tasks/get-sitemap-links.js new file mode 100644 index 00000000000..8e40ac783a2 --- /dev/null +++ b/tasks/get-sitemap-links.js @@ -0,0 +1,42 @@ +const puppeteer = require('puppeteer'); // eslint-disable-line + +const DOMAIN = 'https://docs.amplify.aws'; +const SITEMAP_URL = 'https://docs.amplify.aws/sitemap.xml'; + +const getSitemapUrls = async (localDomain) => { + let browser = await puppeteer.launch({ headless: 'new' }); + + const page = await browser.newPage(); + + let siteMap = localDomain ? `${localDomain}/sitemap.xml` : SITEMAP_URL; + let response = await page.goto(siteMap); + + const siteMapUrls = []; + + if (response && response.status() && response.status() === 200) { + const urlTags = await page.evaluateHandle(() => { + return document.getElementsByTagName('loc'); + }); + const numOfLinks = await page.evaluate((e) => e.length, urlTags); + for (let i = 0; i < numOfLinks; i++) { + let url = await page.evaluate( + (urlTags, i) => urlTags[i].innerHTML, + urlTags, + i + ); + if (localDomain) { + // Currently the sitemap is always generated with the prod docs domain so we need to replace this with localhost + url = url.replace(DOMAIN, localDomain); + } + siteMapUrls.push(url); + } + } + browser.close(); + return siteMapUrls; +}; + +module.exports = { + getSitemapUrls: async (domain) => { + return await getSitemapUrls(domain); + } +}; diff --git a/yarn.lock b/yarn.lock index 2dec273099d..33e00175b74 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5105,10 +5105,10 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -ejs@^3.1.7: - version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" - integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== +ejs@3.1.10, ejs@^3.1.7: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== dependencies: jake "^10.8.5" @@ -5170,11 +5170,6 @@ enquirer@^2.3.6: ansi-colors "^4.1.1" strip-ansi "^6.0.1" -entities@^4.2.0, entities@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" - integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== - env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"