diff --git a/.changeset/lazy-steaks-judge.md b/.changeset/lazy-steaks-judge.md new file mode 100644 index 00000000..bb7a5d9c --- /dev/null +++ b/.changeset/lazy-steaks-judge.md @@ -0,0 +1,5 @@ +--- +"@logto/capacitor": major +--- + +add Capacitor SDK diff --git a/.changeset/mighty-plums-remain.md b/.changeset/mighty-plums-remain.md new file mode 100644 index 00000000..b299bda4 --- /dev/null +++ b/.changeset/mighty-plums-remain.md @@ -0,0 +1,7 @@ +--- +"@logto/browser": patch +"@logto/client": patch +"@logto/js": patch +--- + +add comment annotations for better dev experience diff --git a/packages/browser/README.md b/packages/browser/README.md index 14c95aa8..f02e68e3 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -1,9 +1,9 @@ -# Logto JS (Core) SDK -[![Version](https://img.shields.io/npm/v/@logto/js)](https://www.npmjs.com/package/@logto/js) +# Logto JS Browser SDK +[![Version](https://img.shields.io/npm/v/@logto/browser)](https://www.npmjs.com/package/@logto/browser) [![Build Status](https://github.com/logto-io/js/actions/workflows/main.yml/badge.svg)](https://github.com/logto-io/js/actions/workflows/main.yml) [![Codecov](https://img.shields.io/codecov/c/github/logto-io/js)](https://app.codecov.io/gh/logto-io/js?branch=master) -The Logto JavaScript Core SDK written in TypeScript. Check out our [docs](https://docs.logto.io/JavaScript/browser/) for more information. +The Logto JavaScript Browser SDK written in TypeScript. Check out our [docs](https://docs.logto.io/JavaScript/browser/) for more information. We also provide [文档](https://docs.logto.io/zh-cn/sdk/JavaScript/browser/) in Simplified Chinese. @@ -42,4 +42,4 @@ If Logto does not support your front-end framework and you want to create your o [![Website](https://img.shields.io/badge/website-logto.io-8262F8.svg)](https://logto.io/) [![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/sdk/JavaScript/browser/) -[![Discord](https://img.shields.io/discord/965845662535147551?logo=discord&logoColor=ffffff&color=7389D8&cacheSeconds=600)](https://discord.gg/UEPaF3j5e6) \ No newline at end of file +[![Discord](https://img.shields.io/discord/965845662535147551?logo=discord&logoColor=ffffff&color=7389D8&cacheSeconds=600)](https://discord.gg/UEPaF3j5e6) diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 55454ffa..b55b2b8c 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -6,6 +6,9 @@ import { CacheStorage } from './cache.js'; import { BrowserStorage } from './storage.js'; import { generateCodeChallenge, generateCodeVerifier, generateState } from './utils/generators.js'; +export { createRequester, default as BaseClient } from '@logto/client'; +export { generateCodeChallenge, generateCodeVerifier, generateState } from './utils/generators.js'; + export type { IdTokenClaims, LogtoErrorCode, @@ -13,6 +16,7 @@ export type { LogtoClientErrorCode, UserInfoResponse, InteractionMode, + ClientAdapter, } from '@logto/client'; export { diff --git a/packages/capacitor/CHANGELOG.md b/packages/capacitor/CHANGELOG.md new file mode 100644 index 00000000..b6d1b541 --- /dev/null +++ b/packages/capacitor/CHANGELOG.md @@ -0,0 +1 @@ +# @logto/capacitor diff --git a/packages/capacitor/FixJsdomEnvironment.js b/packages/capacitor/FixJsdomEnvironment.js new file mode 100644 index 00000000..6f776141 --- /dev/null +++ b/packages/capacitor/FixJsdomEnvironment.js @@ -0,0 +1,16 @@ +// https://github.com/microsoft/TypeScript/issues/50690 +// eslint-disable-next-line import/no-named-default +import { default as JSDOMEnvironment } from 'jest-environment-jsdom'; + +// https://github.com/facebook/jest/blob/v29.4.3/website/versioned_docs/version-29.4/Configuration.md#testenvironment-string +export default class FixJsdomEnvironment extends JSDOMEnvironment.default { + constructor(...args) { + super(...args); + + // FIXME https://github.com/jsdom/jsdom/issues/1724 + this.global.fetch = fetch; + this.global.Headers = Headers; + this.global.Request = Request; + this.global.Response = Response; + } +} diff --git a/packages/capacitor/README.md b/packages/capacitor/README.md new file mode 100644 index 00000000..22fb82dd --- /dev/null +++ b/packages/capacitor/README.md @@ -0,0 +1,36 @@ +# Logto JS Capacitor SDK +[![Version](https://img.shields.io/npm/v/@logto/capacitor)](https://www.npmjs.com/package/@logto/capacitor) +[![Build Status](https://github.com/logto-io/js/actions/workflows/main.yml/badge.svg)](https://github.com/logto-io/js/actions/workflows/main.yml) +[![Codecov](https://img.shields.io/codecov/c/github/logto-io/js)](https://app.codecov.io/gh/logto-io/js?branch=master) + +The Logto JavaScript Capacitor SDK written in TypeScript. + +## Installation + +### Using npm + +```bash +npm install @logto/capacitor @capacitor/app @capacitor/browser @capacitor/preferences +``` + +### Using yarn + +```bash +yarn add @logto/capacitor @capacitor/app @capacitor/browser @capacitor/preferences +``` + +### Using pnpm + +```bash +pnpm add @logto/capacitor @capacitor/app @capacitor/browser @capacitor/preferences +``` + +## What is this and how does it work? + +See [Integrate Logto in your application](https://docs.logto.io/docs/recipes/integrate-logto/) for more information. + +## Resources + +[![Website](https://img.shields.io/badge/website-logto.io-8262F8.svg)](https://logto.io/) +[![Docs](https://img.shields.io/badge/docs-logto.io-green.svg)](https://docs.logto.io/sdk/JavaScript/browser/) +[![Discord](https://img.shields.io/discord/965845662535147551?logo=discord&logoColor=ffffff&color=7389D8&cacheSeconds=600)](https://discord.gg/UEPaF3j5e6) diff --git a/packages/capacitor/jest.config.js b/packages/capacitor/jest.config.js new file mode 100644 index 00000000..7d7a5f1e --- /dev/null +++ b/packages/capacitor/jest.config.js @@ -0,0 +1,10 @@ +import baseConfig from '../../jest.config.js'; + +/** @type {import('jest').Config} */ +const config = { + ...baseConfig, + testEnvironment: './FixJsdomEnvironment.js', + setupFilesAfterEnv: ['/jest.setup.js', 'jest-matcher-specific-error'], +}; + +export default config; diff --git a/packages/capacitor/jest.setup.js b/packages/capacitor/jest.setup.js new file mode 100644 index 00000000..29f7ddf3 --- /dev/null +++ b/packages/capacitor/jest.setup.js @@ -0,0 +1,17 @@ +// Need to disable following rules to mock text-decode/text-encoder and crypto for jsdom +// https://github.com/jsdom/jsdom/issues/1612 + +import crypto from 'node:crypto'; + +import { TextDecoder, TextEncoder } from 'text-encoder'; + +/* eslint-disable @silverhand/fp/no-mutation */ +// Mock WebCrypto in JSDOM +if (global.window !== undefined) { + global.CryptoKey = crypto.webcrypto.CryptoKey; + global.crypto.subtle = crypto.webcrypto.subtle; +} + +global.TextDecoder = TextDecoder; +global.TextEncoder = TextEncoder; +/* eslint-enable @silverhand/fp/no-mutation */ diff --git a/packages/capacitor/package.json b/packages/capacitor/package.json new file mode 100644 index 00000000..71751c64 --- /dev/null +++ b/packages/capacitor/package.json @@ -0,0 +1,66 @@ +{ + "name": "@logto/capacitor", + "version": "0.1.0", + "type": "module", + "main": "./lib/index.cjs", + "module": "./lib/index.js", + "types": "./lib/index.d.ts", + "exports": { + "types": "./lib/index.d.ts", + "require": "./lib/index.cjs", + "import": "./lib/index.js", + "default": "./lib/index.js" + }, + "files": [ + "lib" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/logto-io/js.git", + "directory": "packages/capacitor" + }, + "scripts": { + "dev:tsc": "tsc -p tsconfig.build.json -w --preserveWatchOutput", + "precommit": "lint-staged", + "check": "tsc --noEmit", + "build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c", + "lint": "eslint --ext .ts src", + "test": "jest", + "test:coverage": "jest --silent --coverage", + "prepack": "pnpm build && pnpm test" + }, + "dependencies": { + "@logto/browser": "workspace:^2.1.0" + }, + "devDependencies": { + "@capacitor/app": "^5.0.6", + "@capacitor/browser": "^5.0.6", + "@capacitor/preferences": "^5.0.6", + "@silverhand/eslint-config": "^4.0.1", + "@silverhand/ts-config": "^4.0.0", + "@swc/core": "^1.3.50", + "@swc/jest": "^0.2.24", + "@types/jest": "^29.5.0", + "eslint": "^8.44.0", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "jest-matcher-specific-error": "^1.0.0", + "lint-staged": "^13.0.0", + "prettier": "^3.0.0", + "text-encoder": "^0.0.4", + "typescript": "^5.0.0" + }, + "eslintConfig": { + "extends": "@silverhand" + }, + "prettier": "@silverhand/eslint-config/.prettierrc", + "publishConfig": { + "access": "public" + }, + "peerDependencies": { + "@capacitor/app": "^5.0.6", + "@capacitor/browser": "^5.0.6", + "@capacitor/preferences": "^5.0.6" + } +} diff --git a/packages/capacitor/rollup.config.js b/packages/capacitor/rollup.config.js new file mode 100644 index 00000000..049a6eee --- /dev/null +++ b/packages/capacitor/rollup.config.js @@ -0,0 +1 @@ +export { default } from '../../rollup.config.js'; diff --git a/packages/capacitor/src/index.test.ts b/packages/capacitor/src/index.test.ts new file mode 100644 index 00000000..c4dd6a22 --- /dev/null +++ b/packages/capacitor/src/index.test.ts @@ -0,0 +1,35 @@ +import { Browser } from '@capacitor/browser'; + +import CapacitorLogtoClient from './index.js'; + +jest.mock('@capacitor/browser', () => ({ + Browser: { + open: jest.fn(), + }, +})); + +class CapacitorLogtoClientTest extends CapacitorLogtoClient { + getAdapter() { + return this.adapter; + } +} +const createClient = () => + new CapacitorLogtoClientTest({ + endpoint: 'https://your.logto.endpoint', + appId: 'your-app-id', + }); + +describe('CapacitorLogtoClient', () => { + it('should override navigate', async () => { + const client = createClient(); + expect(client.getAdapter().navigate).toBeDefined(); + await client.getAdapter().navigate('https://example.com'); + + const spy = jest.spyOn(Browser, 'open'); + expect(spy).toHaveBeenCalledWith({ + url: 'https://example.com', + windowName: '_self', + presentationStyle: 'popover', + }); + }); +}); diff --git a/packages/capacitor/src/index.ts b/packages/capacitor/src/index.ts new file mode 100644 index 00000000..b2aefa40 --- /dev/null +++ b/packages/capacitor/src/index.ts @@ -0,0 +1,164 @@ +import { App } from '@capacitor/app'; +import { Browser, type OpenOptions } from '@capacitor/browser'; +import { Preferences } from '@capacitor/preferences'; +import LogtoBaseClient, { + LogtoClientError, + type InteractionMode, + type LogtoConfig, +} from '@logto/browser'; + +export type CapacitorConfig = { + /** + * The options to pass to the `open` method of the Capacitor Browser plugin. + * @default { windowName: '_self', presentationStyle: 'popover' } + */ + openOptions?: OpenOptions; +}; + +export default class CapacitorLogtoClient extends LogtoBaseClient { + constructor(config: LogtoConfig, capacitorConfig: CapacitorConfig = {}) { + const { openOptions } = capacitorConfig; + super(config); + + // Use the Capacitor Browser plugin to open the sign-in and sign-out pages + // since the default location assignment method will open the pages in a + // system browser. We need to open an in-app browser to be able to handle + // the redirects back to the app. + // https://capacitorjs.com/docs/apis/browser + this.adapter.navigate = async (url) => { + return Browser.open({ + url, + windowName: '_self', + presentationStyle: 'popover', + ...openOptions, + }); + }; + + // Use the Capacitor Preferences plugin to store the tokens, which will + // fallback to localStorage for web builds. + // https://capacitorjs.com/docs/apis/preferences + this.adapter.storage = { + getItem: async (key) => { + const { value } = await Preferences.get({ key }); + return value; + }, + setItem: async (key, value) => { + await Preferences.set({ key, value }); + }, + removeItem: async (key) => { + await Preferences.remove({ key }); + }, + }; + } + + /** + * Start the sign-in flow with the specified redirect URI. The URI must be + * registered in the Logto Console. + * + * Remember to configure the correct [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links) + * to ensure the app can be opened from the redirect URI. + * + * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed. + * @param interactionMode The interaction mode to be used for the authorization request. Note it's not + * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`. + * @throws {@link LogtoClientError} If error happens during the sign-in flow or the user cancels the sign-in. + * + * @example + * ```ts + * const client = new CapacitorLogtoClient({ + * endpoint: 'https://your.logto.endpoint', + * appId: 'your-app-id', + * }); + * + * await client.signIn('io.logto.example://callback'); // throws if error happens + * console.log(await client.getIdTokenClaims()); // { sub: '123', ... } + * ``` + * + * @remarks + * The user will be redirected to that URI after the sign-in flow is completed, + * and the client will be able to get the authorization code from the URI. + * {@link handleSignInCallback} will be called after the user is redirected. + * + * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information. + * @see {@link InteractionMode} + */ + async signIn(redirectUri: string, interactionMode?: InteractionMode): Promise { + return new Promise((resolve, reject) => { + const run = async () => { + const [browserHandle, appHandle] = await Promise.all([ + // Handle the case where the user closes the browser during the sign-in. + Browser.addListener('browserFinished', async () => { + await Promise.all([browserHandle.remove(), appHandle.remove()]); + reject(new LogtoClientError('user_cancelled')); + }), + // Handle the case where the user completes the sign-in and is redirected + // back to the app. + App.addListener('appUrlOpen', async ({ url }) => { + if (!url.startsWith(redirectUri)) { + return; + } + + await Promise.all([ + // One last step of the sign-in flow + this.handleSignInCallback(url), + // Close the browser and remove the listeners + Browser.close(), + browserHandle.remove(), + appHandle.remove(), + ]); + resolve(); + }), + // Open the in-app browser to start the sign-in flow + super.signIn(redirectUri, interactionMode), + ]); + }; + + void run(); + }); + } + + /** + * Start the sign-out flow with the specified redirect URI. The URI must be + * registered in the Logto Console. + * + * It will also revoke all the tokens and clean up the storage. + * + * - If the `postLogoutRedirectUri` is not specified, the user will see a default + * page after the sign-out flow is completed, they need to close the browser + * manually to return to the app. + * - If the `postLogoutRedirectUri` is specified, the user will be redirected to + * that URI after the sign-out flow is completed. Remember to configure the correct + * [scheme or universal links](https://capacitorjs.com/docs/guides/deep-links) + * to ensure the app can be opened from the redirect URI. + * + * @param postLogoutRedirectUri The URI that the user will be redirected to after the sign-out flow is completed. + * + * @example + * ```ts + * await client.signOut('io.logto.example://callback'); + * ``` + */ + async signOut(postLogoutRedirectUri?: string): Promise { + return new Promise((resolve) => { + const run = async () => { + const [handle] = await Promise.all([ + postLogoutRedirectUri + ? App.addListener('appUrlOpen', async ({ url }) => { + if (postLogoutRedirectUri && !url.startsWith(postLogoutRedirectUri)) { + return; + } + await Promise.all([Browser.close(), handle.remove()]); + resolve(); + }) + : Browser.addListener('browserFinished', async () => { + await handle.remove(); + resolve(); + }), + super.signOut(postLogoutRedirectUri), + ]); + }; + + void run(); + }); + } +} diff --git a/packages/capacitor/tsconfig.build.json b/packages/capacitor/tsconfig.build.json new file mode 100644 index 00000000..77b3df4d --- /dev/null +++ b/packages/capacitor/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig", + "include": [ + "src", + ] +} diff --git a/packages/capacitor/tsconfig.json b/packages/capacitor/tsconfig.json new file mode 100644 index 00000000..05754974 --- /dev/null +++ b/packages/capacitor/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@silverhand/ts-config/tsconfig.base", + "compilerOptions": { + "outDir": "lib", + "types": [ + "jest", + "jest-matcher-specific-error" + ] + }, + "include": [ + "src", + "jest.config.ts", + "FixJsdomEnvironment.ts", + ] +} diff --git a/packages/client/src/errors.ts b/packages/client/src/errors.ts index d2e80798..d85ca899 100644 --- a/packages/client/src/errors.ts +++ b/packages/client/src/errors.ts @@ -3,11 +3,13 @@ const logtoClientErrorCodes = Object.freeze({ 'sign_in_session.not_found': 'Sign-in session not found.', not_authenticated: 'Not authenticated.', fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.', + user_cancelled: 'The user cancelled the action.', }); export type LogtoClientErrorCode = keyof typeof logtoClientErrorCodes; export class LogtoClientError extends Error { + name = 'LogtoClientError'; code: LogtoClientErrorCode; data: unknown; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 56ab4b61..5aa8c416 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -183,7 +183,7 @@ export default class LogtoClient { * Start the sign-in flow with the specified redirect URI. The URI must be * registered in the Logto Console. * - * The user will be redirected that URI after the sign-in flow is completed, + * The user will be redirected to that URI after the sign-in flow is completed, * and the client will be able to get the authorization code from the URI. * To fetch the tokens from the authorization code, use {@link handleSignInCallback} * after the user is redirected in the callback URI. @@ -192,11 +192,10 @@ export default class LogtoClient { * @param interactionMode The interaction mode to be used for the authorization request. Note it's not * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`. * - * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} - * for more information. + * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information. * @see {@link InteractionMode} */ - async signIn(redirectUri: string, interactionMode?: InteractionMode) { + async signIn(redirectUri: string, interactionMode?: InteractionMode): Promise { const { appId: clientId, prompt, resources, scopes } = this.logtoConfig; const { authorizationEndpoint } = await this.getOidcConfig(); const codeVerifier = this.adapter.generateCodeVerifier(); @@ -215,9 +214,11 @@ export default class LogtoClient { interactionMode, }); - await this.setSignInSession({ redirectUri, codeVerifier, state }); - await this.setRefreshToken(null); - await this.setIdToken(null); + await Promise.all([ + this.setSignInSession({ redirectUri, codeVerifier, state }), + this.setRefreshToken(null), + this.setIdToken(null), + ]); await this.adapter.navigate(signInUri); } @@ -304,7 +305,7 @@ export default class LogtoClient { * If the `postLogoutRedirectUri` is not specified, the user will be redirected * to a default page. */ - async signOut(postLogoutRedirectUri?: string) { + async signOut(postLogoutRedirectUri?: string): Promise { const { appId: clientId } = this.logtoConfig; const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig(); const refreshToken = await this.getRefreshToken(); @@ -324,9 +325,12 @@ export default class LogtoClient { }); this.accessTokenMap.clear(); - await this.setRefreshToken(null); - await this.setIdToken(null); - await this.adapter.storage.removeItem('accessToken'); + + await Promise.all([ + this.setRefreshToken(null), + this.setIdToken(null), + this.adapter.storage.removeItem('accessToken'), + ]); await this.adapter.navigate(url); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 67eb3843..b7e7b1a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,6 +140,61 @@ importers: specifier: ^5.0.0 version: 5.0.2 + packages/capacitor: + dependencies: + '@logto/browser': + specifier: workspace:^2.1.0 + version: link:../browser + devDependencies: + '@capacitor/app': + specifier: ^5.0.6 + version: 5.0.6(@capacitor/core@5.2.2) + '@capacitor/browser': + specifier: ^5.0.6 + version: 5.0.6(@capacitor/core@5.2.2) + '@capacitor/preferences': + specifier: ^5.0.6 + version: 5.0.6(@capacitor/core@5.2.2) + '@silverhand/eslint-config': + specifier: ^4.0.1 + version: 4.0.1(eslint@8.44.0)(prettier@3.0.0)(typescript@5.0.2) + '@silverhand/ts-config': + specifier: ^4.0.0 + version: 4.0.0(typescript@5.0.2) + '@swc/core': + specifier: ^1.3.50 + version: 1.3.50 + '@swc/jest': + specifier: ^0.2.24 + version: 0.2.24(@swc/core@1.3.50) + '@types/jest': + specifier: ^29.5.0 + version: 29.5.1 + eslint: + specifier: ^8.44.0 + version: 8.44.0 + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@18.15.11) + jest-environment-jsdom: + specifier: ^29.5.0 + version: 29.5.0 + jest-matcher-specific-error: + specifier: ^1.0.0 + version: 1.0.0 + lint-staged: + specifier: ^13.0.0 + version: 13.0.3 + prettier: + specifier: ^3.0.0 + version: 3.0.0 + text-encoder: + specifier: ^0.0.4 + version: 0.0.4 + typescript: + specifier: ^5.0.0 + version: 5.0.2 + packages/client: dependencies: '@logto/js': @@ -1442,6 +1497,36 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true + /@capacitor/app@5.0.6(@capacitor/core@5.2.2): + resolution: {integrity: sha512-6ZXVdnNmaYILasC/RjQw+yfTmq2ZO7Q3v5lFcDVfq3PFGnybyYQh+RstBrYri+376OmXOXxBD7E6UxBhrMzXGA==} + peerDependencies: + '@capacitor/core': ^5.0.0 + dependencies: + '@capacitor/core': 5.2.2 + dev: true + + /@capacitor/browser@5.0.6(@capacitor/core@5.2.2): + resolution: {integrity: sha512-wEI7Na6PVzSP/00ud7pjbBwXwVG7HywCdy2fJT/hzF6yuHn4tDirbOvbr1JKd9LZqKs2Xn+TapV38JhBRhX6YA==} + peerDependencies: + '@capacitor/core': ^5.0.0 + dependencies: + '@capacitor/core': 5.2.2 + dev: true + + /@capacitor/core@5.2.2: + resolution: {integrity: sha512-3jKECZC5+YD2rljMZm1e/K3AYyoxUmLDZCyofTPbRYPBSI0wJh5ZCkX+XIGzNM0o/Wokl3Voa1JB8xsLC0MPxA==} + dependencies: + tslib: 2.5.0 + dev: true + + /@capacitor/preferences@5.0.6(@capacitor/core@5.2.2): + resolution: {integrity: sha512-aDe4wGTVSAIue6XXdUFgyz7SGszxK/Ptt/iWTydMpzc1PlZXw1XTTnciM+S+SLLNZFzXlkpXT3wMnh9t0DojUA==} + peerDependencies: + '@capacitor/core': ^5.0.0 + dependencies: + '@capacitor/core': 5.2.2 + dev: true + /@changesets/apply-release-plan@6.1.3: resolution: {integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==} dependencies: @@ -8619,7 +8704,7 @@ packages: optional: true dependencies: abab: 2.0.6 - acorn: 8.8.2 + acorn: 8.10.0 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0