From 9154f350c7796de4ca892e7b10eceb34a125e0a2 Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Tue, 29 Aug 2023 07:28:21 +1000 Subject: [PATCH 01/46] fix(dev): get deps from ESM packages without main (#7272) --- .../get-dependencies-from-esm-without-main.md | 5 +++ integration/compiler-test.ts | 32 +++++++++++++++++++ packages/remix-dev/dependencies.ts | 24 +++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 .changeset/get-dependencies-from-esm-without-main.md diff --git a/.changeset/get-dependencies-from-esm-without-main.md b/.changeset/get-dependencies-from-esm-without-main.md new file mode 100644 index 00000000000..614a898bea6 --- /dev/null +++ b/.changeset/get-dependencies-from-esm-without-main.md @@ -0,0 +1,5 @@ +--- +"@remix-run/dev": patch +--- + +Update `getDependenciesToBundle` to handle ESM packages without main exports. Note that these packages must expose `package.json` in their `exports` field so that their path can be resolved. diff --git a/integration/compiler-test.ts b/integration/compiler-test.ts index 116e94abde7..12cbe904c37 100644 --- a/integration/compiler-test.ts +++ b/integration/compiler-test.ts @@ -28,6 +28,7 @@ test.describe("compiler", () => { "esm-only-pkg", "esm-only-single-export", ...getDependenciesToBundle("esm-only-exports-pkg"), + ...getDependenciesToBundle("esm-only-nested-exports-pkg"), ], }; `, @@ -84,6 +85,13 @@ test.describe("compiler", () => { return
{esmOnlyPkg}
; } `, + "app/routes/esm-only-nested-exports-pkg.tsx": js` + import esmOnlyPkg from "esm-only-nested-exports-pkg/nested"; + + export default function EsmOnlyPkg() { + return
{esmOnlyPkg}
; + } + `, "app/routes/esm-only-single-export.tsx": js` import esmOnlyPkg from "esm-only-single-export"; @@ -129,6 +137,18 @@ test.describe("compiler", () => { "node_modules/esm-only-exports-pkg/esm-only-exports-pkg.js": js` export default "esm-only-exports-pkg"; `, + "node_modules/esm-only-nested-exports-pkg/package.json": json({ + name: "esm-only-nested-exports-pkg", + version: "1.0.0", + type: "module", + exports: { + "./package.json": "./package.json", + "./nested": "./esm-only-nested-exports-pkg.js", + }, + }), + "node_modules/esm-only-nested-exports-pkg/esm-only-nested-exports-pkg.js": js` + export default "esm-only-nested-exports-pkg"; + `, "node_modules/esm-only-single-export/package.json": json({ name: "esm-only-exports-pkg", version: "1.0.0", @@ -288,6 +308,18 @@ test.describe("compiler", () => { ); }); + test("allows consumption of ESM modules with only nested exports in CJS builds with `serverDependenciesToBundle` and `getDependenciesToBundle`", async ({ + page, + }) => { + let app = new PlaywrightFixture(appFixture, page); + let res = await app.goto("/esm-only-nested-exports-pkg", true); + expect(res.status()).toBe(200); // server rendered fine + // rendered the page instead of the error boundary + expect(await app.getHtml("#esm-only-nested-exports-pkg")).toBe( + '
esm-only-nested-exports-pkg
' + ); + }); + test("allows consumption of packages with sub modules", async ({ page }) => { let app = new PlaywrightFixture(appFixture, page); let res = await app.goto("/package-with-submodule", true); diff --git a/packages/remix-dev/dependencies.ts b/packages/remix-dev/dependencies.ts index cbf53c10781..e3d6393aab8 100644 --- a/packages/remix-dev/dependencies.ts +++ b/packages/remix-dev/dependencies.ts @@ -40,6 +40,17 @@ export function getDependenciesToBundle(...pkg: string[]): string[] { return Array.from(aggregatedDeps); } +interface ErrorWithCode extends Error { + code: string; +} + +function isErrorWithCode(error: unknown): error is ErrorWithCode { + return ( + error instanceof Error && + typeof (error as NodeJS.ErrnoException).code === "string" + ); +} + function getPackageDependenciesRecursive( pkg: string, aggregatedDeps: Set, @@ -47,7 +58,18 @@ function getPackageDependenciesRecursive( ): void { visitedPackages.add(pkg); - let pkgPath = require.resolve(pkg); + let pkgPath: string; + try { + pkgPath = require.resolve(pkg); + } catch (err) { + if (isErrorWithCode(err) && err.code === "ERR_PACKAGE_PATH_NOT_EXPORTED") { + // Handle packages without main exports. + // They at least need to have package.json exported. + pkgPath = require.resolve(`${pkg}/package.json`); + } else { + throw err; + } + } let lastIndexOfPackageName = pkgPath.lastIndexOf(pkg); if (lastIndexOfPackageName !== -1) { pkgPath = pkgPath.substring(0, lastIndexOfPackageName); From 3e36c8dd8345e9896b91c48382501bf1380613f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= Date: Tue, 29 Aug 2023 17:35:36 +0200 Subject: [PATCH 02/46] chore: update Jest (#7220) --- jest.config.js | 7 +- jest/jest.config.shared.js | 18 +- package.json | 11 +- .../__tests__/create-remix-test.ts | 16 +- packages/create-remix/jest.config.js | 2 +- .../remix-architect/__tests__/server-test.ts | 4 +- packages/remix-architect/jest.config.js | 2 +- packages/remix-dev/__tests__/cli-test.ts | 2 +- .../__tests__/cssSideEffectImports-test.ts | 52 +- .../remix-dev/__tests__/defineRoutes-test.ts | 32 +- .../remix-dev/__tests__/readConfig-test.ts | 18 +- packages/remix-dev/jest.config.js | 2 +- packages/remix-dev/package.json | 1 + packages/remix-eslint-config/README.md | 2 +- packages/remix-eslint-config/package.json | 2 +- .../remix-express/__tests__/server-test.ts | 4 +- packages/remix-express/jest.config.js | 2 +- packages/remix-node/jest.config.js | 2 +- .../__tests__/integration/meta-test.tsx | 18 +- packages/remix-react/jest.config.js | 2 +- packages/remix-react/package.json | 1 + .../__tests__/cookies-test.ts | 4 +- packages/remix-server-runtime/jest.config.js | 2 +- packages/remix-testing/jest.config.js | 2 +- packages/remix-testing/package.json | 1 + yarn.lock | 1838 +++++++++-------- 26 files changed, 1052 insertions(+), 995 deletions(-) diff --git a/jest.config.js b/jest.config.js index e2c605cdae6..6fe429db3d7 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { modulePathIgnorePatterns: [ "/.tmp", @@ -23,10 +23,13 @@ module.exports = { "packages/remix-server-runtime", "packages/remix-testing", ], + reporters: + process.env.GITHUB_ACTIONS === null + ? ["default"] + : [["github-actions", { silent: false }], "summary"], watchPlugins: [ require.resolve("jest-watch-select-projects"), require.resolve("jest-watch-typeahead/filename"), require.resolve("jest-watch-typeahead/testname"), ], - reporters: ["default"], }; diff --git a/jest/jest.config.shared.js b/jest/jest.config.shared.js index b1d1a89167e..547a2da9a6a 100644 --- a/jest/jest.config.shared.js +++ b/jest/jest.config.shared.js @@ -5,16 +5,26 @@ const ignorePatterns = [ "\\/\\.tmp\\/", "\\/\\.cache\\/", ]; -/** @type {import('@jest/types').Config.InitialOptions} */ + +/** @type {import('jest').Config} */ module.exports = { - testEnvironment: "node", + moduleNameMapper: { + "^@remix-run/web-blob$": require.resolve("@remix-run/web-blob"), + "^@remix-run/web-fetch$": require.resolve("@remix-run/web-fetch"), + "^@remix-run/web-file": require.resolve("@remix-run/web-file"), + "^@remix-run/web-form-data$": require.resolve("@remix-run/web-form-data"), + "^@remix-run/web-stream$": require.resolve("@remix-run/web-stream"), + "^@web3-storage/multipart-parser$": require.resolve( + "@web3-storage/multipart-parser" + ), + }, modulePathIgnorePatterns: ignorePatterns, - watchPathIgnorePatterns: [...ignorePatterns, "\\/node_modules\\/"], - testMatch: ["/**/*-test.[jt]s?(x)"], setupFiles: ["/__tests__/setup.ts"], + testMatch: ["/**/*-test.[jt]s?(x)"], transform: { "\\.[jt]sx?$": require.resolve("./transform"), }, + watchPathIgnorePatterns: [...ignorePatterns, "\\/node_modules\\/"], watchPlugins: [ require.resolve("jest-watch-select-projects"), require.resolve("jest-watch-typeahead/filename"), diff --git a/package.json b/package.json index 4cf6784e521..9b8c1953829 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,9 @@ "watch": "rollup -c --watch --watch.onEnd=\"node scripts/copy-build-to-dist.mjs\"", "postinstall": "patch-package" }, + "resolutions": { + "jsdom": "^22.0.0" + }, "dependencies": { "@babel/core": "^7.21.8", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", @@ -74,7 +77,7 @@ "@types/cross-spawn": "^6.0.2", "@types/dedent": "^0.7.0", "@types/glob": "7.2.0", - "@types/jest": "^27.4.1", + "@types/jest": "^29.5.4", "@types/jsonfile": "^6.1.0", "@types/lodash": "^4.14.182", "@types/react": "^18.2.20", @@ -84,7 +87,7 @@ "@types/semver": "^7.3.4", "@types/serialize-javascript": "^5.0.2", "@types/ssri": "^7.1.0", - "babel-jest": "^27.5.1", + "babel-jest": "^29.6.4", "babel-plugin-transform-remove-console": "^6.9.4", "chalk": "^4.1.2", "concurrently": "^7.0.0", @@ -95,9 +98,9 @@ "eslint-plugin-prefer-let": "^3.0.1", "front-matter": "^4.0.2", "glob": "8.0.3", - "jest": "^27.5.1", + "jest": "^29.6.4", "jest-watch-select-projects": "^2.0.0", - "jest-watch-typeahead": "^0.6.5", + "jest-watch-typeahead": "^2.2.2", "jsonfile": "^6.0.1", "lodash": "^4.17.21", "mime": "^3.0.0", diff --git a/packages/create-remix/__tests__/create-remix-test.ts b/packages/create-remix/__tests__/create-remix-test.ts index bc15019c927..dd4e8803d66 100644 --- a/packages/create-remix/__tests__/create-remix-test.ts +++ b/packages/create-remix/__tests__/create-remix-test.ts @@ -242,7 +242,7 @@ describe("create-remix CLI", () => { }); expect(stderr.trim()).toMatchInlineSnapshot( - `"▲ Oh no! The path \\"this/path/does/not/exist\\" was not found in this GitHub repo."` + `"▲ Oh no! The path "this/path/does/not/exist" was not found in this GitHub repo."` ); expect(status).toBe(1); expect(fse.existsSync(path.join(projectDir, "package.json"))).toBeFalsy(); @@ -381,7 +381,7 @@ describe("create-remix CLI", () => { }); expect(stderr.trim()).toMatchInlineSnapshot( - `"▲ Oh no! The path \\"this/path/does/not/exist\\" was not found in this GitHub repo."` + `"▲ Oh no! The path "this/path/does/not/exist" was not found in this GitHub repo."` ); expect(status).toBe(1); expect(fse.existsSync(path.join(projectDir, "package.json"))).toBeFalsy(); @@ -1086,12 +1086,12 @@ describe("create-remix CLI", () => { }); expect(stderr.trim()).toMatchInlineSnapshot(` - "▲ Oh no! Destination directory contains files that would be overwritten - and no \`--overwrite\` flag was included in a non-interactive - environment. The following files would be overwritten: - package.json - tsconfig.json" - `); + "▲ Oh no! Destination directory contains files that would be overwritten + and no \`--overwrite\` flag was included in a non-interactive + environment. The following files would be overwritten: + package.json + tsconfig.json" + `); expect(status).toBe(1); expect( fse.existsSync(path.join(projectDir, "app/root.tsx")) diff --git a/packages/create-remix/jest.config.js b/packages/create-remix/jest.config.js index bdcc536c0e7..1e25ee332f7 100644 --- a/packages/create-remix/jest.config.js +++ b/packages/create-remix/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "create-remix", diff --git a/packages/remix-architect/__tests__/server-test.ts b/packages/remix-architect/__tests__/server-test.ts index f32c07aac49..2f7d8ed54f1 100644 --- a/packages/remix-architect/__tests__/server-test.ts +++ b/packages/remix-architect/__tests__/server-test.ts @@ -201,9 +201,7 @@ describe("architect createRemixHeaders", () => { describe("creates fetch headers from architect headers", () => { it("handles empty headers", () => { let headers = createRemixHeaders({}); - expect(Object.fromEntries(headers.entries())).toMatchInlineSnapshot( - `Object {}` - ); + expect(Object.fromEntries(headers.entries())).toMatchInlineSnapshot(`{}`); }); it("handles simple headers", () => { diff --git a/packages/remix-architect/jest.config.js b/packages/remix-architect/jest.config.js index e784c337988..94bdf00603d 100644 --- a/packages/remix-architect/jest.config.js +++ b/packages/remix-architect/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "architect", diff --git a/packages/remix-dev/__tests__/cli-test.ts b/packages/remix-dev/__tests__/cli-test.ts index e517da737ab..49c11c8945a 100644 --- a/packages/remix-dev/__tests__/cli-test.ts +++ b/packages/remix-dev/__tests__/cli-test.ts @@ -128,7 +128,7 @@ describe("remix CLI", () => { Run your project locally in development: $ remix dev - $ remix dev -c \\"node ./server.js\\" + $ remix dev -c "node ./server.js" Start your server separately and watch for changes: diff --git a/packages/remix-dev/__tests__/cssSideEffectImports-test.ts b/packages/remix-dev/__tests__/cssSideEffectImports-test.ts index 55e3f7a2413..b3abf5c02c5 100644 --- a/packages/remix-dev/__tests__/cssSideEffectImports-test.ts +++ b/packages/remix-dev/__tests__/cssSideEffectImports-test.ts @@ -10,7 +10,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"require(\\"./foo.css?__remix_sideEffect__\\");"` + `"require("./foo.css?__remix_sideEffect__");"` ); }); @@ -20,7 +20,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"import \\"./foo.css?__remix_sideEffect__\\";"` + `"import "./foo.css?__remix_sideEffect__";"` ); }); @@ -33,7 +33,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("jsx", code)) .toMatchInlineSnapshot(` - "import \\"./foo.css?__remix_sideEffect__\\"; + "import "./foo.css?__remix_sideEffect__"; export const Foo = () =>
;" `); @@ -48,7 +48,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("ts", code)) .toMatchInlineSnapshot(` - "require(\\"./foo.css?__remix_sideEffect__\\"); + "require("./foo.css?__remix_sideEffect__"); export const foo: string = ('foo' satisfies string);" `); @@ -64,7 +64,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("tsx", code)) .toMatchInlineSnapshot(` - "require(\\"./foo.css?__remix_sideEffect__\\"); + "require("./foo.css?__remix_sideEffect__"); export const foo: string = ('foo' satisfies string); export const Bar = () =>
{foo}
;" @@ -82,10 +82,10 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("js", code)) .toMatchInlineSnapshot(` - "if (process.env.NODE_ENV === \\"production\\") { - require(\\"./foo.min.css?__remix_sideEffect__\\"); + "if (process.env.NODE_ENV === "production") { + require("./foo.min.css?__remix_sideEffect__"); } else { - require(\\"./foo.css?__remix_sideEffect__\\"); + require("./foo.css?__remix_sideEffect__"); }" `); }); @@ -96,7 +96,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"process.env.NODE_ENV === \\"production\\" ? require(\\"./foo.min.css?__remix_sideEffect__\\") : require(\\"./foo.css?__remix_sideEffect__\\");"` + `"process.env.NODE_ENV === "production" ? require("./foo.min.css?__remix_sideEffect__") : require("./foo.css?__remix_sideEffect__");"` ); }); @@ -106,7 +106,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"process.env.NODE_ENV === \\"development\\" && require(\\"./debug.css?__remix_sideEffect__\\");"` + `"process.env.NODE_ENV === "development" && require("./debug.css?__remix_sideEffect__");"` ); }); @@ -116,7 +116,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"process.env.NODE_ENV === \\"production\\" || require(\\"./debug.css?__remix_sideEffect__\\");"` + `"process.env.NODE_ENV === "production" || require("./debug.css?__remix_sideEffect__");"` ); }); }); @@ -128,7 +128,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"const href = require(\\"./foo.css\\");"` + `"const href = require("./foo.css");"` ); }); @@ -138,7 +138,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"import href from \\"./foo.css\\";"` + `"import href from "./foo.css";"` ); }); @@ -148,7 +148,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"import { foo } from \\"./foo.css\\";"` + `"import { foo } from "./foo.css";"` ); }); @@ -158,7 +158,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"import * as foo from \\"./foo.css\\";"` + `"import * as foo from "./foo.css";"` ); }); @@ -171,9 +171,9 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("js", code)) .toMatchInlineSnapshot(` - "const href = process.env.NODE_ENV === \\"production\\" ? - require(\\"./foo.min.css\\") : - require(\\"./foo.css\\");" + "const href = process.env.NODE_ENV === "production" ? + require("./foo.min.css") : + require("./foo.css");" `); }); @@ -183,7 +183,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"const href = process.env.NODE_ENV === \\"production\\" && require(\\"./foo.min.css\\") || require(\\"./foo.css\\");"` + `"const href = process.env.NODE_ENV === "production" && require("./foo.min.css") || require("./foo.css");"` ); }); @@ -193,7 +193,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"require(\\"./foo\\");"` + `"require("./foo");"` ); }); @@ -203,7 +203,7 @@ describe("addSuffixToCssSideEffectImports", () => { `; expect(addSuffixToCssSideEffectImports("js", code)).toMatchInlineSnapshot( - `"import \\"./foo\\";"` + `"import "./foo";"` ); }); @@ -217,7 +217,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("js", code)) .toMatchInlineSnapshot(` "export const foo = async () => { - await import(\\"./foo.css\\"); + await import("./foo.css"); };" `); }); @@ -288,7 +288,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("js", code)) .toMatchInlineSnapshot(` - "require(\\"./foo.css?__remix_sideEffect__\\"); + "require("./foo.css?__remix_sideEffect__"); // JS const topLevelAwait = await Promise.resolve('top level await'); @@ -330,7 +330,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("jsx", code)) .toMatchInlineSnapshot(` - "require(\\"./foo.css?__remix_sideEffect__\\"); + "require("./foo.css?__remix_sideEffect__"); // JSX const ExampleComponent = () =>
JSX element
; @@ -375,7 +375,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("tsx", code)) .toMatchInlineSnapshot(` - "require(\\"./foo.css?__remix_sideEffect__\\"); + "require("./foo.css?__remix_sideEffect__"); // TS const exampleSatisfies = ('satisfies' satisfies string); @@ -425,7 +425,7 @@ describe("addSuffixToCssSideEffectImports", () => { expect(addSuffixToCssSideEffectImports("tsx", code)) .toMatchInlineSnapshot(` - "require(\\"./foo.css?__remix_sideEffect__\\"); + "require("./foo.css?__remix_sideEffect__"); // TS const exampleSatisfies = ('satisfies' satisfies string); diff --git a/packages/remix-dev/__tests__/defineRoutes-test.ts b/packages/remix-dev/__tests__/defineRoutes-test.ts index 31371037ecd..44f73e99511 100644 --- a/packages/remix-dev/__tests__/defineRoutes-test.ts +++ b/packages/remix-dev/__tests__/defineRoutes-test.ts @@ -12,8 +12,8 @@ describe("defineRoutes", () => { }); expect(routes).toMatchInlineSnapshot(` - Object { - "routes/home": Object { + { + "routes/home": { "caseSensitive": undefined, "file": "routes/home.js", "id": "routes/home", @@ -21,7 +21,7 @@ describe("defineRoutes", () => { "parentId": "root", "path": "/", }, - "routes/inbox": Object { + "routes/inbox": { "caseSensitive": undefined, "file": "routes/inbox.js", "id": "routes/inbox", @@ -29,7 +29,7 @@ describe("defineRoutes", () => { "parentId": "root", "path": "inbox", }, - "routes/inbox/$messageId": Object { + "routes/inbox/$messageId": { "caseSensitive": undefined, "file": "routes/inbox/$messageId.js", "id": "routes/inbox/$messageId", @@ -37,7 +37,7 @@ describe("defineRoutes", () => { "parentId": "routes/inbox", "path": ":messageId", }, - "routes/inbox/archive": Object { + "routes/inbox/archive": { "caseSensitive": undefined, "file": "routes/inbox/archive.js", "id": "routes/inbox/archive", @@ -45,7 +45,7 @@ describe("defineRoutes", () => { "parentId": "routes/inbox", "path": "archive", }, - "routes/inbox/index": Object { + "routes/inbox/index": { "caseSensitive": undefined, "file": "routes/inbox/index.js", "id": "routes/inbox/index", @@ -67,8 +67,8 @@ describe("defineRoutes", () => { }); expect(routes).toMatchInlineSnapshot(` - Object { - "one": Object { + { + "one": { "caseSensitive": undefined, "file": "one.md", "id": "one", @@ -76,7 +76,7 @@ describe("defineRoutes", () => { "parentId": "root", "path": "one", }, - "two": Object { + "two": { "caseSensitive": undefined, "file": "two.md", "id": "two", @@ -96,8 +96,8 @@ describe("defineRoutes", () => { }); expect(routes).toMatchInlineSnapshot(` - Object { - "routes/other-route": Object { + { + "routes/other-route": { "caseSensitive": undefined, "file": "routes/other-route.tsx", "id": "routes/other-route", @@ -105,7 +105,7 @@ describe("defineRoutes", () => { "parentId": "root", "path": "/other", }, - "user": Object { + "user": { "caseSensitive": undefined, "file": "routes/_index.tsx", "id": "user", @@ -113,7 +113,7 @@ describe("defineRoutes", () => { "parentId": "root", "path": "/user", }, - "user-by-id": Object { + "user-by-id": { "caseSensitive": undefined, "file": "routes/_index.tsx", "id": "user-by-id", @@ -136,7 +136,7 @@ describe("defineRoutes", () => { }; expect(defineNonUniqueRoutes).toThrowErrorMatchingInlineSnapshot( - `"Unable to define routes with duplicate route id: \\"user\\""` + `"Unable to define routes with duplicate route id: "user""` ); // Custom id conflicting with a later-defined auto-generated id @@ -148,7 +148,7 @@ describe("defineRoutes", () => { }; expect(defineNonUniqueRoutes).toThrowErrorMatchingInlineSnapshot( - `"Unable to define routes with duplicate route id: \\"routes/user\\""` + `"Unable to define routes with duplicate route id: "routes/user""` ); // Custom id conflicting with an earlier-defined auto-generated id @@ -160,7 +160,7 @@ describe("defineRoutes", () => { }; expect(defineNonUniqueRoutes).toThrowErrorMatchingInlineSnapshot( - `"Unable to define routes with duplicate route id: \\"routes/user\\""` + `"Unable to define routes with duplicate route id: "routes/user""` ); }); }); diff --git a/packages/remix-dev/__tests__/readConfig-test.ts b/packages/remix-dev/__tests__/readConfig-test.ts index 62d3cee196a..ec4532eee04 100644 --- a/packages/remix-dev/__tests__/readConfig-test.ts +++ b/packages/remix-dev/__tests__/readConfig-test.ts @@ -25,34 +25,34 @@ describe("readConfig", () => { tsconfigPath: expect.any(String), }, ` - Object { + { "appDirectory": Any, "assetsBuildDirectory": Any, "cacheDirectory": Any, - "dev": Object {}, + "dev": {}, "entryClientFile": "entry.client.tsx", "entryClientFilePath": Any, "entryServerFile": "entry.server.tsx", "entryServerFilePath": Any, - "future": Object {}, + "future": {}, "mdx": undefined, "postcss": true, "publicPath": "/build/", "relativeAssetsBuildDirectory": Any, "rootDirectory": Any, - "routes": Object { - "root": Object { + "routes": { + "root": { "file": "root.tsx", "id": "root", "path": "", }, }, "serverBuildPath": Any, - "serverBuildTargetEntryModule": "export * from \\"@remix-run/dev/server-build\\";", + "serverBuildTargetEntryModule": "export * from "@remix-run/dev/server-build";", "serverConditions": undefined, - "serverDependenciesToBundle": Array [], + "serverDependenciesToBundle": [], "serverEntryPoint": undefined, - "serverMainFields": Array [ + "serverMainFields": [ "module", "main", ], @@ -63,7 +63,7 @@ describe("readConfig", () => { "serverPlatform": "node", "tailwind": true, "tsconfigPath": Any, - "watchPaths": Array [], + "watchPaths": [], } ` ); diff --git a/packages/remix-dev/jest.config.js b/packages/remix-dev/jest.config.js index bd8918937b0..3f4869bc95f 100644 --- a/packages/remix-dev/jest.config.js +++ b/packages/remix-dev/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "dev", diff --git a/packages/remix-dev/package.json b/packages/remix-dev/package.json index 129f287466b..b8027f64e1c 100644 --- a/packages/remix-dev/package.json +++ b/packages/remix-dev/package.json @@ -78,6 +78,7 @@ "@types/node-fetch": "^2.5.7", "@types/npmcli__package-json": "^4.0.0", "@types/picomatch": "^2.3.0", + "@types/prettier": "^2.7.3", "@types/shelljs": "^0.8.11", "@types/tar-fs": "^2.0.1", "@types/ws": "^7.4.1", diff --git a/packages/remix-eslint-config/README.md b/packages/remix-eslint-config/README.md index 44ef8a23427..80d768806a4 100644 --- a/packages/remix-eslint-config/README.md +++ b/packages/remix-eslint-config/README.md @@ -40,7 +40,7 @@ Please note that because this ruleset is optional, we do not include the core li "dependencies": { "@testing-library/jest-dom": ">=5.16.0", "@testing-library/react": ">=12.0.0", - "jest": ">=26.0.0" + "jest": ">=28.0.0" } } ``` diff --git a/packages/remix-eslint-config/package.json b/packages/remix-eslint-config/package.json index c8ba3302ac3..3b349442310 100644 --- a/packages/remix-eslint-config/package.json +++ b/packages/remix-eslint-config/package.json @@ -43,7 +43,7 @@ "devDependencies": { "@types/eslint": "^8.37.0", "eslint": "^8.37.0", - "jest": "^27.5.1", + "jest": "^29.6.4", "react": "^18.2.0", "typescript": "^5.1.6" }, diff --git a/packages/remix-express/__tests__/server-test.ts b/packages/remix-express/__tests__/server-test.ts index 75f9e835671..ae7cc305499 100644 --- a/packages/remix-express/__tests__/server-test.ts +++ b/packages/remix-express/__tests__/server-test.ts @@ -158,9 +158,7 @@ describe("express createRemixHeaders", () => { describe("creates fetch headers from express headers", () => { it("handles empty headers", () => { let headers = createRemixHeaders({}); - expect(Object.fromEntries(headers.entries())).toMatchInlineSnapshot( - `Object {}` - ); + expect(Object.fromEntries(headers.entries())).toMatchInlineSnapshot(`{}`); }); it("handles simple headers", () => { diff --git a/packages/remix-express/jest.config.js b/packages/remix-express/jest.config.js index 063327efe81..0587714c3b3 100644 --- a/packages/remix-express/jest.config.js +++ b/packages/remix-express/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "express", diff --git a/packages/remix-node/jest.config.js b/packages/remix-node/jest.config.js index dec6a65773c..3327ee72ff6 100644 --- a/packages/remix-node/jest.config.js +++ b/packages/remix-node/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "node", diff --git a/packages/remix-react/__tests__/integration/meta-test.tsx b/packages/remix-react/__tests__/integration/meta-test.tsx index 39ce5e06aca..0a830249b13 100644 --- a/packages/remix-react/__tests__/integration/meta-test.tsx +++ b/packages/remix-react/__tests__/integration/meta-test.tsx @@ -51,8 +51,8 @@ describe("meta", () => { expect(getHtml(container)).toMatchInlineSnapshot(` "
Meta Page @@ -127,7 +127,7 @@ describe("meta", () => { expect(getHtml(container)).toMatchInlineSnapshot(` "<div> <meta - charset=\\"utf-8\\" + charset="utf-8" /> <title> Child title @@ -153,7 +153,7 @@ describe("meta", () => { expect(getHtml(container)).toMatchInlineSnapshot(` "<div> <meta - charset=\\"utf-8\\" + charset="utf-8" /> </div>" `); @@ -195,11 +195,11 @@ describe("meta", () => { expect(getHtml(container)).toMatchInlineSnapshot(` "<div> <meta - content=\\"https://picsum.photos/200/200\\" - property=\\"og:image\\" + content="https://picsum.photos/200/200" + property="og:image" /> <meta - property=\\"og:type\\" + property="og:type" /> </div>" `); @@ -261,8 +261,8 @@ describe("meta", () => { expect(getHtml(container)).toMatchInlineSnapshot(` "<div> <link - href=\\"https://website.com/authors/1\\" - rel=\\"canonical\\" + href="https://website.com/authors/1" + rel="canonical" /> </div>" `); diff --git a/packages/remix-react/jest.config.js b/packages/remix-react/jest.config.js index 045bc40bc66..6cc1a0c9d79 100644 --- a/packages/remix-react/jest.config.js +++ b/packages/remix-react/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "react", diff --git a/packages/remix-react/package.json b/packages/remix-react/package.json index d529afbaaf9..2a72416e342 100644 --- a/packages/remix-react/package.json +++ b/packages/remix-react/package.json @@ -24,6 +24,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.3.0", "@types/react": "^18.2.20", + "jest-environment-jsdom": "^29.6.4", "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.1.6" diff --git a/packages/remix-server-runtime/__tests__/cookies-test.ts b/packages/remix-server-runtime/__tests__/cookies-test.ts index 30e6c4f6803..e58a7f1332b 100644 --- a/packages/remix-server-runtime/__tests__/cookies-test.ts +++ b/packages/remix-server-runtime/__tests__/cookies-test.ts @@ -96,7 +96,7 @@ describe("cookies", () => { let value = await cookie.parse(getCookieFromSetCookie(setCookie)); expect(value).toMatchInlineSnapshot(` - Object { + { "hello": "mjackson", } `); @@ -123,7 +123,7 @@ describe("cookies", () => { let value = await cookie.parse(getCookieFromSetCookie(setCookie)); expect(value).toMatchInlineSnapshot(` - Object { + { "hello": "mjackson", } `); diff --git a/packages/remix-server-runtime/jest.config.js b/packages/remix-server-runtime/jest.config.js index b3bd1f18308..92a3431fdc5 100644 --- a/packages/remix-server-runtime/jest.config.js +++ b/packages/remix-server-runtime/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "server-runtime", diff --git a/packages/remix-testing/jest.config.js b/packages/remix-testing/jest.config.js index 928c47b3ae5..483aa32c42d 100644 --- a/packages/remix-testing/jest.config.js +++ b/packages/remix-testing/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ +/** @type {import('jest').Config} */ module.exports = { ...require("../../jest/jest.config.shared"), displayName: "testing", diff --git a/packages/remix-testing/package.json b/packages/remix-testing/package.json index 78545580012..66cde9f62b1 100644 --- a/packages/remix-testing/package.json +++ b/packages/remix-testing/package.json @@ -25,6 +25,7 @@ "@types/node": "^18.17.1", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", + "jest-environment-jsdom": "^29.6.4", "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.1.6" diff --git a/yarn.lock b/yarn.lock index dbe9fe2fb08..d31aeb92187 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28,38 +28,39 @@ run-waterfall "^1.1.7" uid-safe "^2.1.5" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" + integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.22.10" + chalk "^2.4.2" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": - version "7.21.7" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz#61caffb60776e49a57ba61a88f02bedd8714f6bc" - integrity sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.20.7", "@babel/core@^7.21.8", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.21.8" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" - integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.7", "@babel/core@^7.21.8": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" + integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helpers" "^7.21.5" - "@babel/parser" "^7.21.8" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.10" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.10" + "@babel/parser" "^7.22.10" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" - semver "^6.3.0" + semver "^6.3.1" "@babel/eslint-parser@^7.21.8": version "7.21.8" @@ -70,12 +71,12 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.21.5", "@babel/generator@^7.7.2": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz#c0c0e5449504c7b7de8236d99338c3e2a340745f" - integrity sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w== +"@babel/generator@^7.21.5", "@babel/generator@^7.22.10", "@babel/generator@^7.7.2": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" + integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== dependencies: - "@babel/types" "^7.21.5" + "@babel/types" "^7.22.10" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -95,16 +96,16 @@ "@babel/helper-explode-assignable-expression" "^7.18.6" "@babel/types" "^7.18.6" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" - integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.10": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" + integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== dependencies: - "@babel/compat-data" "^7.21.5" - "@babel/helper-validator-option" "^7.21.0" - browserslist "^4.21.3" + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": version "7.21.8" @@ -142,10 +143,10 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" - integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5", "@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== "@babel/helper-explode-assignable-expression@^7.18.6": version "7.18.6" @@ -154,20 +155,20 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== +"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" "@babel/helper-member-expression-to-functions@^7.21.5": version "7.21.5" @@ -176,26 +177,23 @@ dependencies: "@babel/types" "^7.21.5" -"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" - integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.22.5" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" - integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== dependencies: - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-simple-access" "^7.21.5" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" @@ -231,12 +229,12 @@ "@babel/traverse" "^7.21.5" "@babel/types" "^7.21.5" -"@babel/helper-simple-access@^7.21.5": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" - integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== +"@babel/helper-simple-access@^7.21.5", "@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== dependencies: - "@babel/types" "^7.21.5" + "@babel/types" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers@^7.20.0": version "7.20.0" @@ -245,27 +243,27 @@ dependencies: "@babel/types" "^7.20.0" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.21.5": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" - integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0", "@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== "@babel/helper-wrap-function@^7.18.9": version "7.20.5" @@ -277,28 +275,28 @@ "@babel/traverse" "^7.20.5" "@babel/types" "^7.20.5" -"@babel/helpers@^7.21.5": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" - integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== +"@babel/helpers@^7.22.10": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" + integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== +"@babel/highlight@^7.22.10": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" + integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8": - version "7.21.8" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz#642af7d0333eab9c0ad70b14ac5e76dbde7bfdf8" - integrity sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.21.8", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" + integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" @@ -515,12 +513,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4": - version "7.21.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" - integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== +"@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -1007,38 +1005,38 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" -"@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5", "@babel/traverse@^7.7.2": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" - integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.5" - "@babel/types" "^7.21.5" +"@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5", "@babel/traverse@^7.22.10": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" + integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== + dependencies: + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.10" + "@babel/types" "^7.22.10" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.21.5" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" - integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.21.5", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.10" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" + integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== dependencies: - "@babel/helper-string-parser" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1618,173 +1616,196 @@ resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== +"@jest/console@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz#a7e2d84516301f986bba0dd55af9d5fe37f46527" + integrity sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.6.3" + jest-util "^29.6.3" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== - dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" +"@jest/core@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz#265ebee05ec1ff3567757e7a327155c8d6bdb126" + integrity sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg== + dependencies: + "@jest/console" "^29.6.4" + "@jest/reporters" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" + jest-changed-files "^29.6.3" + jest-config "^29.6.4" + jest-haste-map "^29.6.4" + jest-message-util "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.6.4" + jest-resolve-dependencies "^29.6.4" + jest-runner "^29.6.4" + jest-runtime "^29.6.4" + jest-snapshot "^29.6.4" + jest-util "^29.6.3" + jest-validate "^29.6.3" + jest-watcher "^29.6.4" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.6.3" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz#78ec2c9f8c8829a37616934ff4fea0c028c79f4f" + integrity sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "^29.6.4" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "^29.6.3" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/expect-utils@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz#17c7dfe6cec106441f218b0aff4b295f98346679" + integrity sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + jest-get-type "^29.6.3" + +"@jest/expect@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz#1d6ae17dc68d906776198389427ab7ce6179dba6" + integrity sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA== + dependencies: + expect "^29.6.4" + jest-snapshot "^29.6.4" + +"@jest/fake-timers@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz#45a27f093c43d5d989362a3e7a8c70c83188b4f6" + integrity sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw== + dependencies: + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.6.3" + jest-mock "^29.6.3" + jest-util "^29.6.3" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/globals@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz#4f04f58731b062b44ef23036b79bdb31f40c7f63" + integrity sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/environment" "^29.6.4" + "@jest/expect" "^29.6.4" + "@jest/types" "^29.6.3" + jest-mock "^29.6.3" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz#9d6350c8a2761ece91f7946e97ab0dabc06deab7" + integrity sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.6.3" + jest-util "^29.6.3" + jest-worker "^29.6.4" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" - source-map "^0.6.0" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/test-result@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz#adf5c79f6e1fb7405ad13d67d9e2b6ff54b54c6b" + integrity sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.6.4" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-sequencer@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz#86aef66aaa22b181307ed06c26c82802fb836d7b" + integrity sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg== dependencies: - "@jest/test-result" "^27.5.1" + "@jest/test-result" "^29.6.4" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + jest-haste-map "^29.6.4" + slash "^3.0.0" -"@jest/transform@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/transform@^29.6.4": + version "29.6.4" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz#a6bc799ef597c5d85b2e65a11fd96b6b239bab5a" + integrity sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.6.4" + jest-regex-util "^29.6.3" + jest-util "^29.6.3" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + write-file-atomic "^4.0.2" -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^16.0.0" + "@types/yargs" "^17.0.8" chalk "^4.0.0" "@jridgewell/gen-mapping@^0.1.0": @@ -1804,28 +1825,28 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13": - version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@jspm/core@^2.0.1": version "2.0.1" @@ -2237,6 +2258,11 @@ resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz" integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sindresorhus/slugify@2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.1.0.tgz" @@ -2253,19 +2279,19 @@ escape-string-regexp "^5.0.0" lodash.deburr "^4.1.0" -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^10.0.2": + version "10.3.0" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^3.0.0" "@testing-library/cypress@^8.0.2": version "8.0.2" @@ -2313,10 +2339,10 @@ "@testing-library/dom" "^8.5.0" "@types/react-dom" "^18.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@tootallnate/quickjs-emscripten@^0.23.0": version "0.23.0" @@ -2340,7 +2366,7 @@ resolved "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.83.tgz" integrity sha512-7YsLv/B8rF7K7jYAGmYBxLq3QU+hQV7qNJBMcSCmJCTcXuzoTKGBX8d4v9CsVs0SOKBSAErXG7rtk8jVxiP30g== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14": version "7.1.18" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.18.tgz" integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== @@ -2366,7 +2392,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.14.2" resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz" integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== @@ -2519,10 +2545,10 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" @@ -2566,19 +2592,28 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*", "@types/jest@^27.4.1": - version "27.4.1" - resolved "https://registry.npmjs.org/@types/jest/-/jest-27.4.1.tgz" - integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw== +"@types/jest@*", "@types/jest@^29.5.4": + version "29.5.4" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz#9d0a16edaa009a71e6a71a999acd582514dab566" + integrity sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A== dependencies: - jest-matcher-utils "^27.0.0" - pretty-format "^27.0.0" + expect "^29.0.0" + pretty-format "^29.0.0" "@types/js-levenshtein@^1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz" integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g== +"@types/jsdom@^20.0.0": + version "20.0.1" + resolved "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz#07c14bc19bd2f918c1929541cdaacae894744808" + integrity sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/jsesc@^3.0.1": version "3.0.1" resolved "https://registry.npmjs.org/@types/jsesc/-/jsesc-3.0.1.tgz" @@ -2707,10 +2742,10 @@ resolved "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== -"@types/prettier@^2.1.5": - version "2.4.4" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz" - integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== +"@types/prettier@^2.7.3": + version "2.7.3" + resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/prop-types@*": version "15.7.4" @@ -2878,6 +2913,11 @@ dependencies: "@types/jest" "*" +"@types/tough-cookie@*": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" + integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + "@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.6" resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" @@ -2895,10 +2935,10 @@ resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== -"@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" @@ -3061,10 +3101,10 @@ resolved "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz" integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== -abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== abort-controller@^3.0.0: version "3.0.0" @@ -3081,33 +3121,15 @@ accepts@^1.3.7, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - acorn-jsx@^5.0.0, acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.0.0, acorn@^8.2.4, acorn@^8.8.0, acorn@^8.8.1: - version "8.8.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.0.0, acorn@^8.8.0, acorn@^8.8.1: + version "8.10.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -3275,7 +3297,7 @@ ansi-dim@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -3289,6 +3311,13 @@ ansi-escapes@^5.0.0: dependencies: type-fest "^1.0.2" +ansi-escapes@^6.0.0: + version "6.2.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz#8a13ce75286f417f1963487d86ba9f90dccf9947" + integrity sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw== + dependencies: + type-fest "^3.0.0" + ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz" @@ -3629,16 +3658,15 @@ axobject-query@^3.1.1: dependencies: deep-equal "^2.0.5" -babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== +babel-jest@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz#98dbc45d1c93319c82a8ab4a478b670655dd2585" + integrity sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw== dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/transform" "^29.6.4" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" + babel-preset-jest "^29.6.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -3654,14 +3682,14 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" babel-plugin-polyfill-corejs2@^0.3.3: @@ -3711,12 +3739,12 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^27.5.1" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" bail@^2.0.0: @@ -3839,11 +3867,6 @@ breakword@^1.0.5: dependencies: wcwidth "^1.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browserify-zlib@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz" @@ -3851,15 +3874,15 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== +browserslist@^4.21.5, browserslist@^4.21.9: + version "4.21.10" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" bser@2.1.1: version "2.1.1" @@ -3973,10 +3996,10 @@ camelcase@^6.2.0: resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001449: - version "1.0.30001487" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001487.tgz#d882d1a34d89c11aea53b8cdc791931bdab5fe1b" - integrity sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA== +caniuse-lite@^1.0.30001517: + version "1.0.30001522" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" + integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== caseless@~0.12.0: version "0.12.0" @@ -3996,7 +4019,7 @@ chalk@4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1: +chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4021,11 +4044,21 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +char-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e" + integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== + character-entities-html4@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.0.0.tgz" @@ -4373,13 +4406,18 @@ content-type@~1.0.4: resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" @@ -4498,22 +4536,12 @@ cssesc@^3.0.0: resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== dependencies: - cssom "~0.3.6" + rrweb-cssom "^0.6.0" csstype@^3.0.2, csstype@^3.0.7: version "3.1.1" @@ -4615,14 +4643,14 @@ data-uri-to-buffer@^5.0.1: resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz#db89a9e279c2ffe74f50637a59a32fb23b3e4d7c" integrity sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" dataloader@^1.4.0: version "1.4.0" @@ -4673,10 +4701,10 @@ decamelize@^1.1.0, decamelize@^1.2.0: resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -decimal.js@^10.2.1: - version "10.3.1" - resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== decode-named-character-reference@^1.0.0: version "1.0.2" @@ -4690,6 +4718,11 @@ dedent@^0.7.0: resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + deep-equal@^2.0.5: version "2.2.0" resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" @@ -4832,10 +4865,10 @@ didyoumean@^1.2.2: resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^5.0.0: version "5.1.0" @@ -4887,12 +4920,12 @@ domelementtype@^2.3.0: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== dependencies: - webidl-conversions "^5.0.0" + webidl-conversions "^7.0.0" domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" @@ -4953,15 +4986,15 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.4.284: - version "1.4.394" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.394.tgz#989abe104a40366755648876cde2cdeda9f31133" - integrity sha512-0IbC2cfr8w5LxTz+nmn2cJTGafsK9iauV2r5A5scfzyovqLrxuLoxOHE5OBobP3oVIggJT+0JfKnw9sm87c8Hw== +electron-to-chromium@^1.4.477: + version "1.4.499" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.499.tgz#dc36b67f4c8e273524e8d2080c5203a6a76987b6" + integrity sha512-0NmjlYBLKVHva4GABWAaHuPJolnDuL0AhV3h1hES6rcLCWEIbRL6/8TghfsVwkx6TEroQVdliX7+aLysUpKvjw== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -5202,7 +5235,7 @@ escape-string-regexp@^5.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz" integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== -escodegen@^2.0.0, escodegen@^2.1.0: +escodegen@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== @@ -5634,15 +5667,16 @@ exit@^0.1.2: resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect@^29.0.0, expect@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz#a6e6f66d4613717859b2fe3da98a739437b6f4b8" + integrity sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.6.4" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-util "^29.6.3" express@^4.17.1: version "4.17.3" @@ -5749,9 +5783,9 @@ fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: @@ -6190,7 +6224,7 @@ glob@^10.2.2: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: version "7.2.0" resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -6431,12 +6465,12 @@ hosted-git-info@^6.0.0, hosted-git-info@^6.1.1: dependencies: lru-cache "^7.5.1" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== dependencies: - whatwg-encoding "^1.0.5" + whatwg-encoding "^2.0.0" html-escaper@^2.0.0: version "2.0.2" @@ -6464,12 +6498,12 @@ http-errors@1.8.1: statuses ">= 1.5.0 < 2" toidentifier "1.0.1" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== dependencies: - "@tootallnate/once" "1" + "@tootallnate/once" "2" agent-base "6" debug "4" @@ -6490,10 +6524,10 @@ http-signature@~1.3.6: jsprim "^2.0.2" sshpk "^1.14.1" -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" @@ -6528,6 +6562,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" @@ -7052,7 +7093,7 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" -is-typedarray@^1.0.0, is-typedarray@~1.0.0: +is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -7129,7 +7170,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.1.0" resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== @@ -7140,6 +7181,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz#7a8af094cbfff1d5bb280f62ce043695ae8dd5b8" + integrity sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" @@ -7180,379 +7232,344 @@ javascript-stringify@^2.0.1: resolved "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== +jest-changed-files@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz#97cfdc93f74fb8af2a1acb0b78f836f1fb40c449" + integrity sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg== dependencies: - "@jest/types" "^27.5.1" execa "^5.0.0" - throat "^6.0.1" - -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + jest-util "^29.6.3" + p-limit "^3.1.0" + +jest-circus@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz#f074c8d795e0cc0f2ebf0705086b1be6a9a8722f" + integrity sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/expect" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" - expect "^27.5.1" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-each "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-runtime "^29.6.4" + jest-snapshot "^29.6.4" + jest-util "^29.6.3" + p-limit "^3.1.0" + pretty-format "^29.6.3" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-cli@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz#ad52f2dfa1b0291de7ec7f8d7c81ac435521ede0" + integrity sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/core" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/types" "^29.6.3" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-config "^29.6.4" + jest-util "^29.6.3" + jest-validate "^29.6.3" prompts "^2.0.1" - yargs "^16.2.0" + yargs "^17.3.1" -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== +jest-config@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz#eff958ee41d4e1ee7a6106d02b74ad9fc427d79e" + integrity sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A== dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.6.4" + "@jest/types" "^29.6.3" + babel-jest "^29.6.4" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" + glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-circus "^29.6.4" + jest-environment-node "^29.6.4" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.6.4" + jest-runner "^29.6.4" + jest-util "^29.6.3" + jest-validate "^29.6.3" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^29.6.3" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz#85aaa6c92a79ae8cd9a54ebae8d5b6d9a513314a" + integrity sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== +jest-docblock@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz#293dca5188846c9f7c0c2b1bb33e5b11f21645f2" + integrity sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ== dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== +jest-each@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz#1956f14f5f0cb8ae0b2e7cabc10bb03ec817c142" + integrity sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + jest-get-type "^29.6.3" + jest-util "^29.6.3" + pretty-format "^29.6.3" + +jest-environment-jsdom@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.6.4.tgz#0daf44454041f9e1ef7fa82eb1bd43426a82eb1c" + integrity sha512-K6wfgUJ16DoMs02JYFid9lOsqfpoVtyJxpRlnTxUHzvZWBnnh2VNGRB9EC1Cro96TQdq5TtSjb3qUjNaJP9IyA== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/fake-timers" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/jsdom" "^20.0.0" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + jest-mock "^29.6.3" + jest-util "^29.6.3" + jsdom "^20.0.0" + +jest-environment-node@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz#4ce311549afd815d3cafb49e60a1e4b25f06d29f" + integrity sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/fake-timers" "^29.6.4" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-mock "^29.6.3" + jest-util "^29.6.3" -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== +jest-haste-map@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz#97143ce833829157ea7025204b08f9ace609b96a" + integrity sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog== dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.6.3" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-regex-util "^29.6.3" + jest-util "^29.6.3" + jest-worker "^29.6.4" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== +jest-leak-detector@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz#b9661bc3aec8874e59aff361fa0c6d7cd507ea01" + integrity sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q== dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" -jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz#327db7ababea49455df3b23e5d6109fe0c709d24" + integrity sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.6.4" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz#bce16050d86801b165f20cfde34dc01d3cf85fbf" + integrity sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.6.3" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz#433f3fd528c8ec5a76860177484940628bdf5e0a" + integrity sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" + jest-util "^29.6.3" jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^27.0.0, jest-regex-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== +jest-regex-util@^29.0.0, jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== +jest-resolve-dependencies@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz#20156b33c7eacbb6bb77aeba4bed0eab4a3f8734" + integrity sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA== dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" + jest-regex-util "^29.6.3" + jest-snapshot "^29.6.4" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== +jest-resolve@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz#e34cb06f2178b429c38455d98d1a07572ac9faa3" + integrity sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q== dependencies: - "@jest/types" "^27.5.1" chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" + jest-haste-map "^29.6.4" jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-util "^29.6.3" + jest-validate "^29.6.3" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== - dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" +jest-runner@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz#b3b8ccb85970fde0fae40c73ee11eb75adccfacf" + integrity sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw== + dependencies: + "@jest/console" "^29.6.4" + "@jest/environment" "^29.6.4" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + jest-docblock "^29.6.3" + jest-environment-node "^29.6.4" + jest-haste-map "^29.6.4" + jest-leak-detector "^29.6.3" + jest-message-util "^29.6.3" + jest-resolve "^29.6.4" + jest-runtime "^29.6.4" + jest-util "^29.6.3" + jest-watcher "^29.6.4" + jest-worker "^29.6.4" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz#b0bc495c9b6b12a0a7042ac34ca9bb85f8cd0ded" + integrity sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA== + dependencies: + "@jest/environment" "^29.6.4" + "@jest/fake-timers" "^29.6.4" + "@jest/globals" "^29.6.4" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.6.4" + jest-message-util "^29.6.3" + jest-mock "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.6.4" + jest-snapshot "^29.6.4" + jest-util "^29.6.3" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== +jest-snapshot@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz#9833eb6b66ff1541c7fd8ceaa42d541f407b4876" + integrity sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA== dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" - "@types/prettier" "^2.1.5" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.6.4" + "@jest/transform" "^29.6.4" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.5.1" + expect "^29.6.4" graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-diff "^29.6.4" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.6.4" + jest-message-util "^29.6.3" + jest-util "^29.6.3" natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" + pretty-format "^29.6.3" + semver "^7.5.3" -jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== +jest-util@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz#e15c3eac8716440d1ed076f09bc63ace1aebca63" + integrity sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== +jest-validate@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz#a75fca774cfb1c5758c70d035d30a1f9c2784b4d" + integrity sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.5.1" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^27.5.1" + pretty-format "^29.6.3" jest-watch-select-projects@^2.0.0: version "2.0.0" @@ -7563,49 +7580,52 @@ jest-watch-select-projects@^2.0.0: chalk "^3.0.0" prompts "^2.2.1" -jest-watch-typeahead@^0.6.5: - version "0.6.5" - resolved "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-0.6.5.tgz" - integrity sha512-GIbV6h37/isatMDtqZlA8Q5vC6T3w+5qdvtF+3LIkPc58zEWzbKmTHvlUIp3wvBm400RzrQWcVPcsAJqKWu7XQ== - dependencies: - ansi-escapes "^4.3.1" - chalk "^4.0.0" - jest-regex-util "^27.0.0" - jest-watcher "^27.0.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" +jest-watch-typeahead@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-2.2.2.tgz#5516d3cd006485caa5cfc9bd1de40f1f8b136abf" + integrity sha512-+QgOFW4o5Xlgd6jGS5X37i08tuuXNW8X0CV9WNFi+3n8ExCIP+E1melYhvYLjv5fE6D0yyzk74vsSO8I6GqtvQ== + dependencies: + ansi-escapes "^6.0.0" + chalk "^5.2.0" + jest-regex-util "^29.0.0" + jest-watcher "^29.0.0" + slash "^5.0.0" + string-length "^5.0.1" + strip-ansi "^7.0.1" -jest-watcher@^27.0.0, jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== +jest-watcher@^29.0.0, jest-watcher@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz#633eb515ae284aa67fd6831f1c9d1b534cf0e0ba" + integrity sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ== dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/test-result" "^29.6.4" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.5.1" + emittery "^0.13.1" + jest-util "^29.6.3" string-length "^4.0.1" -jest-worker@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +jest-worker@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz#f34279f4afc33c872b470d4af21b281ac616abd3" + integrity sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q== dependencies: "@types/node" "*" + jest-util "^29.6.3" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest@^29.6.4: + version "29.6.4" + resolved "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz#7c48e67a445ba264b778253b5d78d4ebc9d0a622" + integrity sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw== dependencies: - "@jest/core" "^27.5.1" + "@jest/core" "^29.6.4" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^27.5.1" + jest-cli "^29.6.4" jiti@^1.17.2: version "1.18.2" @@ -7647,38 +7667,34 @@ jsbn@~0.1.0: resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" +jsdom@^20.0.0, jsdom@^22.0.0: + version "22.1.0" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8" + integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw== + dependencies: + abab "^2.0.6" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" + domexception "^4.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" jsesc@3.0.2: version "3.0.2" @@ -7984,7 +8000,7 @@ lodash.startcase@^4.4.0: resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz" integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== -lodash@^4.17.15, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -8102,12 +8118,12 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: - tmpl "1.0.x" + tmpl "1.0.5" map-obj@^1.0.0: version "1.0.1" @@ -9066,10 +9082,10 @@ node-mocks-http@^1.10.1: range-parser "^1.2.0" type-is "^1.6.18" -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== node-webtokens@^1.0.4: version "1.0.4" @@ -9162,10 +9178,10 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== +nwsapi@^2.2.4: + version "2.2.7" + resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" @@ -9365,7 +9381,7 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -9507,12 +9523,7 @@ parse5-htmlparser2-tree-adapter@^7.0.0: domhandler "^5.0.2" parse5 "^7.0.0" -parse5@6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -parse5@^7.0.0: +parse5@^7.0.0, parse5@^7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -9839,7 +9850,7 @@ pretty-bytes@^5.6.0: resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: +pretty-format@^27.0.2: version "27.5.1" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -9848,6 +9859,15 @@ pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^29.0.0, pretty-format@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" + integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-ms@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz" @@ -10032,10 +10052,15 @@ pumpify@^1.3.3: inherits "^2.0.3" pump "^2.0.0" -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +pure-rand@^6.0.0: + version "6.0.2" + resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" + integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== qs@6.9.7: version "6.9.7" @@ -10054,6 +10079,11 @@ qs@~6.5.2: resolved "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz" integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" @@ -10123,6 +10153,11 @@ react-is@^17.0.1: resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + react-refresh@^0.14.0: version "0.14.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz" @@ -10431,6 +10466,11 @@ requireindex@^1.2.0, requireindex@~1.2.0: resolved "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz" integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -10448,12 +10488,7 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - -resolve.exports@^2.0.2: +resolve.exports@^2.0.0, resolve.exports@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== @@ -10558,6 +10593,11 @@ rollup@^3.21.0: optionalDependencies: fsevents "~2.3.2" +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" @@ -10615,15 +10655,15 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== dependencies: xmlchars "^2.2.0" @@ -10639,12 +10679,12 @@ scheduler@^0.23.0: resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3: +semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -10779,7 +10819,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -10818,6 +10858,11 @@ slash@^4.0.0: resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slash@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz" @@ -10900,7 +10945,15 @@ source-map-js@^1.0.2: resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-support@^0.5.21, source-map-support@^0.5.6: +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.5.21: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -11058,6 +11111,14 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-length@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz#3d647f497b6e8e8d41e422f7e0b23bc536c8381e" + integrity sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow== + dependencies: + char-regex "^2.0.0" + strip-ansi "^7.0.1" + "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -11273,7 +11334,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -11287,14 +11348,6 @@ supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" @@ -11386,14 +11439,6 @@ term-size@^2.1.0: resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz" integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - terminal-paginator@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/terminal-paginator/-/terminal-paginator-2.0.2.tgz" @@ -11431,11 +11476,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throat@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== - throttleit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz" @@ -11486,9 +11526,9 @@ tmp@~0.2.1: dependencies: rimraf "^3.0.0" -tmpl@1.0.x: +tmpl@1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: @@ -11535,14 +11575,15 @@ toml@^3.0.0: resolved "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== +tough-cookie@^4.1.2: + version "4.1.3" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== dependencies: psl "^1.1.33" punycode "^2.1.1" - universalify "^0.1.2" + universalify "^0.2.0" + url-parse "^1.5.3" tough-cookie@~2.5.0: version "2.5.0" @@ -11552,12 +11593,12 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== dependencies: - punycode "^2.1.1" + punycode "^2.3.0" tr46@~0.0.3: version "0.0.3" @@ -11702,6 +11743,11 @@ type-fest@^2.19.0: resolved "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + type-fest@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-4.0.0.tgz#1c87b05c3f0304a2f911b5a47bb45fa4d62031db" @@ -11724,13 +11770,6 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - typescript@^5.1.0, typescript@^5.1.6: version "5.1.6" resolved "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" @@ -11907,11 +11946,16 @@ universal-user-agent@^6.0.0: resolved "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + universalify@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" @@ -11927,7 +11971,7 @@ untildify@^4.0.0: resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -update-browserslist-db@^1.0.10: +update-browserslist-db@^1.0.11: version "1.0.11" resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== @@ -11942,6 +11986,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -11984,14 +12036,14 @@ uvu@^0.5.0: kleur "^4.0.3" sade "^1.7.3" -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.1: + version "9.1.0" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.4: version "3.0.4" @@ -12075,26 +12127,19 @@ vite-node@^0.28.5: optionalDependencies: fsevents "~2.3.2" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== +w3c-xmlserializer@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" + integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== dependencies: - xml-name-validator "^3.0.0" + xml-name-validator "^4.0.0" -walker@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: - makeerror "1.0.x" + makeerror "1.0.12" warning-symbol@^0.1.0: version "0.1.0" @@ -12127,27 +12172,30 @@ webidl-conversions@^3.0.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== dependencies: - iconv-lite "0.4.24" + iconv-lite "0.6.3" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" whatwg-url@^5.0.0: version "5.0.0" @@ -12157,15 +12205,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" @@ -12287,25 +12326,28 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" + signal-exit "^3.0.7" -ws@^7.4.5, ws@^7.4.6: +ws@^7.4.5: version "7.5.7" resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== xmlchars@^2.2.0: version "2.2.0" From 5b5bb9b93c9cf6ca2078d20a5627e1dc12780297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= <info@michaeldeboey.be> Date: Tue, 29 Aug 2023 19:51:07 +0200 Subject: [PATCH 03/46] chore: cleanup `createFixtureProject` in integration test helpers (#7264) --- integration/helpers/create-fixture.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/integration/helpers/create-fixture.ts b/integration/helpers/create-fixture.ts index c513017ff4c..39061195064 100644 --- a/integration/helpers/create-fixture.ts +++ b/integration/helpers/create-fixture.ts @@ -327,11 +327,6 @@ async function writeTestFiles(init: FixtureInit, dir: string) { let filePath = path.join(dir, filename); await fse.ensureDir(path.dirname(filePath)); let file = init.files![filename]; - // if we have a jsconfig we don't want the tsconfig to exist - if (filename.endsWith("jsconfig.json")) { - let parsed = path.parse(filePath); - await fse.remove(path.join(parsed.dir, "tsconfig.json")); - } await fse.writeFile(filePath, stripIndent(file)); }) From 517e40bae90ddec7a94a93f06c206cf93363210f Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Wed, 30 Aug 2023 10:43:00 -0400 Subject: [PATCH 04/46] Fix CI env check from PR comment in #7220 --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 6fe429db3d7..5ffd3c56699 100644 --- a/jest.config.js +++ b/jest.config.js @@ -24,7 +24,7 @@ module.exports = { "packages/remix-testing", ], reporters: - process.env.GITHUB_ACTIONS === null + process.env.GITHUB_ACTIONS == null ? ["default"] : [["github-actions", { silent: false }], "summary"], watchPlugins: [ From 18f6c3803d1e9da7db8a4ad1ba0dd8a93470ed01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= <info@michaeldeboey.be> Date: Wed, 30 Aug 2023 19:54:28 +0200 Subject: [PATCH 05/46] test: remove polyfill references from Node adapters (#7294) --- packages/remix-architect/__tests__/server-test.ts | 13 +++++-------- packages/remix-express/__tests__/server-test.ts | 11 ++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/remix-architect/__tests__/server-test.ts b/packages/remix-architect/__tests__/server-test.ts index 2f7d8ed54f1..7cbfce23f96 100644 --- a/packages/remix-architect/__tests__/server-test.ts +++ b/packages/remix-architect/__tests__/server-test.ts @@ -1,11 +1,8 @@ import fsp from "node:fs/promises"; import path from "node:path"; -import lambdaTester from "lambda-tester"; +import { createRequestHandler as createRemixRequestHandler } from "@remix-run/node"; import type { APIGatewayProxyEventV2 } from "aws-lambda"; -import { - createRequestHandler as createRemixRequestHandler, - Response as NodeResponse, -} from "@remix-run/node"; +import lambdaTester from "lambda-tester"; import { createRequestHandler, @@ -249,14 +246,14 @@ describe("architect createRemixRequest", () => { describe("sendRemixResponse", () => { it("handles regular responses", async () => { - let response = new NodeResponse("anything"); + let response = new Response("anything"); let result = await sendRemixResponse(response); expect(result.body).toBe("anything"); }); it("handles resource routes with regular data", async () => { let json = JSON.stringify({ foo: "bar" }); - let response = new NodeResponse(json, { + let response = new Response(json, { headers: { "Content-Type": "application/json", "content-length": json.length.toString(), @@ -271,7 +268,7 @@ describe("sendRemixResponse", () => { it("handles resource routes with binary data", async () => { let image = await fsp.readFile(path.join(__dirname, "554828.jpeg")); - let response = new NodeResponse(image, { + let response = new Response(image, { headers: { "content-type": "image/jpeg", "content-length": image.length.toString(), diff --git a/packages/remix-express/__tests__/server-test.ts b/packages/remix-express/__tests__/server-test.ts index ae7cc305499..a35a881cd31 100644 --- a/packages/remix-express/__tests__/server-test.ts +++ b/packages/remix-express/__tests__/server-test.ts @@ -1,11 +1,8 @@ +import { Readable } from "node:stream"; +import { createRequestHandler as createRemixRequestHandler } from "@remix-run/node"; import express from "express"; -import supertest from "supertest"; import { createRequest, createResponse } from "node-mocks-http"; -import { - createRequestHandler as createRemixRequestHandler, - Response as NodeResponse, -} from "@remix-run/node"; -import { Readable } from "node:stream"; +import supertest from "supertest"; import { createRemixHeaders, @@ -103,7 +100,7 @@ describe("express createRequestHandler", () => { it("handles body as stream", async () => { mockedCreateRequestHandler.mockImplementation(() => async () => { let stream = Readable.from("hello world"); - return new NodeResponse(stream, { status: 200 }) as unknown as Response; + return new Response(stream, { status: 200 }) as unknown as Response; }); let request = supertest(createApp()); From 00d1f390b12c78a75fd7e5e7a85b7cbbaf28684f Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Wed, 30 Aug 2023 15:09:22 -0400 Subject: [PATCH 06/46] Update CI workflows to cancel based on built-in concurrency setting (#7279) --- .github/workflows/deployments.yml | 19 ++++--------------- .github/workflows/format.yml | 7 ++++--- .github/workflows/lint.yml | 7 ++++--- .github/workflows/nightly.yml | 7 ++++--- .github/workflows/release.yml | 6 ------ .github/workflows/stacks.yml | 19 ++++--------------- 6 files changed, 20 insertions(+), 45 deletions(-) diff --git a/.github/workflows/deployments.yml b/.github/workflows/deployments.yml index d53ec737fbb..1d679c56304 100644 --- a/.github/workflows/deployments.yml +++ b/.github/workflows/deployments.yml @@ -22,15 +22,16 @@ on: TEST_FLY_TOKEN: required: true +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: arc_deploy: name: Architect Deploy if: github.repository == 'remix-run/remix' runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 @@ -65,9 +66,6 @@ jobs: if: github.repository == 'remix-run/remix' runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 @@ -103,9 +101,6 @@ jobs: if: github.repository == 'remix-run/remix' runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 @@ -142,9 +137,6 @@ jobs: if: github.repository == 'remix-run/remix' runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 @@ -184,9 +176,6 @@ jobs: if: github.repository == 'remix-run/remix' runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 58b7b24a092..b204b9c7e60 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -6,15 +6,16 @@ on: - main - dev +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: format: if: github.repository == 'remix-run/remix' runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cbc5ffff3eb..c1cbeca482e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,14 +7,15 @@ on: - dev pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: lint: name: ⬣ Lint runs-on: ubuntu-latest steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a184b16d35b..6f28ce46680 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -5,6 +5,10 @@ on: schedule: - cron: "0 7 * * *" # every day at 12AM PST +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: CI: true @@ -22,9 +26,6 @@ jobs: # allows this to be used in the `comment` job below - will be undefined if there's no release necessary NEXT_VERSION: ${{ steps.version.outputs.NEXT_VERSION }} steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f2194d2d58..a6ab5209286 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,9 +24,6 @@ jobs: published_packages: ${{ steps.changesets.outputs.publishedPackages }} published: ${{ steps.changesets.outputs.published }} steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 with: @@ -72,9 +69,6 @@ jobs: outputs: package_version: ${{ steps.find_package_version.outputs.package_version }} steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⬇️ Checkout repo uses: actions/checkout@v3 diff --git a/.github/workflows/stacks.yml b/.github/workflows/stacks.yml index 7aeffde1954..de73ff07237 100644 --- a/.github/workflows/stacks.yml +++ b/.github/workflows/stacks.yml @@ -7,6 +7,10 @@ on: required: true type: string +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: setup: name: Remix Stacks Test @@ -23,9 +27,6 @@ jobs: - repo: "remix-run/grunge-stack" name: "grunge" steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: ⎔ Setup node uses: actions/setup-node@v3 with: @@ -80,9 +81,6 @@ jobs: - repo: "remix-run/grunge-stack" name: "grunge" steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: 🗄️ Restore ${{ matrix.stack.name }} uses: actions/download-artifact@v3 with: @@ -123,9 +121,6 @@ jobs: - repo: "remix-run/grunge-stack" name: "grunge" steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: 🗄️ Restore ${{ matrix.stack.name }} uses: actions/download-artifact@v3 with: @@ -166,9 +161,6 @@ jobs: - repo: "remix-run/grunge-stack" name: "grunge" steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: 🗄️ Restore ${{ matrix.stack.name }} uses: actions/download-artifact@v3 with: @@ -212,9 +204,6 @@ jobs: name: "grunge" cypress: "npm run dev" steps: - - name: 🛑 Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.11.0 - - name: 🗄️ Restore ${{ matrix.stack.name }} uses: actions/download-artifact@v3 with: From 10d95f03b05c5fad9639b91254c75c9b92f1c3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= <info@michaeldeboey.be> Date: Wed, 30 Aug 2023 21:52:09 +0200 Subject: [PATCH 07/46] feat(remix-node): use Web Crypto API in `createFileSessionStorage` (#7203) Co-authored-by: Matt Brophy <matt@brophy.org> --- .changeset/node-web-crypto.md | 5 +++++ packages/remix-node/sessions/fileStorage.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/node-web-crypto.md diff --git a/.changeset/node-web-crypto.md b/.changeset/node-web-crypto.md new file mode 100644 index 00000000000..483ca4ecb2f --- /dev/null +++ b/.changeset/node-web-crypto.md @@ -0,0 +1,5 @@ +--- +"@remix-run/node": patch +--- + +Switch from `crypto.randomBytes` to `crypto.webcrypto.getRandomValues` for file session storage ID generation diff --git a/packages/remix-node/sessions/fileStorage.ts b/packages/remix-node/sessions/fileStorage.ts index 042539710cf..fc756093704 100644 --- a/packages/remix-node/sessions/fileStorage.ts +++ b/packages/remix-node/sessions/fileStorage.ts @@ -42,7 +42,7 @@ export function createFileSessionStorage<Data = SessionData, FlashData = Data>({ while (true) { // TODO: Once Node v19 is supported we should use the globally provided // Web Crypto API's crypto.getRandomValues() function here instead. - let randomBytes = crypto.randomBytes(8); + let randomBytes = crypto.webcrypto.getRandomValues(new Uint8Array(8)); // This storage manages an id space of 2^64 ids, which is far greater // than the maximum number of files allowed on an NTFS or ext4 volume // (2^32). However, the larger id space should help to avoid collisions From a9edc944e7f7b0292da8ce03ad8850aaa3342c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20De=20Boey?= <info@michaeldeboey.be> Date: Wed, 30 Aug 2023 21:53:38 +0200 Subject: [PATCH 08/46] refactor: use native `Blob` class (#7217) Co-authored-by: Matt Brophy <matt@brophy.org> --- .changeset/use-native-blob.md | 5 +++++ packages/remix-node/fetch.ts | 2 +- packages/remix-node/globals.ts | 3 --- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .changeset/use-native-blob.md diff --git a/.changeset/use-native-blob.md b/.changeset/use-native-blob.md new file mode 100644 index 00000000000..32d0122ed86 --- /dev/null +++ b/.changeset/use-native-blob.md @@ -0,0 +1,5 @@ +--- +"@remix-run/node": patch +--- + +Use native `Blob` cleass instead of polyfill diff --git a/packages/remix-node/fetch.ts b/packages/remix-node/fetch.ts index 98d9b24dd9f..2b10041e698 100644 --- a/packages/remix-node/fetch.ts +++ b/packages/remix-node/fetch.ts @@ -7,7 +7,7 @@ import { } from "@remix-run/web-fetch"; export { FormData } from "@remix-run/web-fetch"; // @ts-ignore -export { File, Blob } from "@remix-run/web-file"; +export { File } from "@remix-run/web-file"; type NodeHeadersInit = ConstructorParameters<typeof WebHeaders>[0]; type NodeResponseInfo = ConstructorParameters<typeof WebResponse>[0]; diff --git a/packages/remix-node/globals.ts b/packages/remix-node/globals.ts index 2732cbe17c4..35015ebc1c0 100644 --- a/packages/remix-node/globals.ts +++ b/packages/remix-node/globals.ts @@ -5,7 +5,6 @@ import { import { atob, btoa } from "./base64"; import { - Blob as NodeBlob, File as NodeFile, FormData as NodeFormData, Headers as NodeHeaders, @@ -24,7 +23,6 @@ declare global { atob: typeof atob; btoa: typeof btoa; - Blob: typeof Blob; File: typeof File; Headers: typeof Headers; @@ -43,7 +41,6 @@ export function installGlobals() { global.atob = atob; global.btoa = btoa; - global.Blob = NodeBlob; global.File = NodeFile; global.Headers = NodeHeaders as typeof Headers; From 4fd343d2934da01192d817dd7971edf552ef6b9c Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Thu, 31 Aug 2023 10:01:55 -0400 Subject: [PATCH 09/46] Add docs on error santization (#7315) --- docs/guides/errors.md | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docs/guides/errors.md b/docs/guides/errors.md index 0a1f73af3b3..7f2ee28586a 100644 --- a/docs/guides/errors.md +++ b/docs/guides/errors.md @@ -61,3 +61,48 @@ If a route doesn't have an error boundary, the error "bubbles up" to the closest [error-boundary]: ../route/error-boundary [error-in-a-nested-route-where-the-parent-route-s-navigation-renders-normally]: /docs-images/error-boundary.png + +## Error Sanitization + +In production mode, any errors that happen on the server are automatically sanitized to prevent leaking any sensitive server information (such as stack traces) to the client. This means that the `Error` instance you receive from `useRouteError` will have a generic message and no stack trace: + +```jsx +export function loader() { + if (badConditionIsTrue()) { + throw new Error("Oh no! Something went wrong!"); + } +} + +export function ErrorBoundary() { + let error = useRouteError(); + // When NODE_ENV=production: + // error.message = "Unexpected Server Error" + // error.stack = undefined +} +``` + +If you need to log these errors or report then to a third-party service such as [Sentry][sentry] or [BugSnag][bugsnag], then you can do this through a [`handleError`][handleerror] export in your `entry.server.js`. This method receives the un-sanitized versions of the error since it it also running on the server. + +If you want to trigger an error boundary and display a specific message or data in the browser, then you can throw a `Response` from a `loader`/`action` with that data instead: + +```jsx +export function loader() { + if (badConditionIsTrue()) { + throw new Response("Oh no! Something went wrong!", { + status: 500, + }); + } +} + +export function ErrorBoundary() { + let error = useRouteError(); + if (isRouteErrorResponse(error)) { + // error.status = 500 + // error.data = "Oh no! Something went wrong!" + } +} +``` + +[handleerror]: ../file-conventions/entry.server#handleerror +[sentry]: https://sentry.io/ +[bugsnag]: https://www.bugsnag.com/ From 048d0ad32f2c8e533fcb13051e1a464cdf01084a Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Thu, 31 Aug 2023 14:03:38 +0000 Subject: [PATCH 10/46] chore: format --- docs/guides/errors.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/errors.md b/docs/guides/errors.md index 7f2ee28586a..11fe0fc1a63 100644 --- a/docs/guides/errors.md +++ b/docs/guides/errors.md @@ -74,7 +74,7 @@ export function loader() { } export function ErrorBoundary() { - let error = useRouteError(); + const error = useRouteError(); // When NODE_ENV=production: // error.message = "Unexpected Server Error" // error.stack = undefined @@ -95,7 +95,7 @@ export function loader() { } export function ErrorBoundary() { - let error = useRouteError(); + const error = useRouteError(); if (isRouteErrorResponse(error)) { // error.status = 500 // error.data = "Oh no! Something went wrong!" From 4892a0ddb13b824c2c63f3fd829e07a0a9b76e8e Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 29 Aug 2023 11:45:51 -0600 Subject: [PATCH 11/46] docs: form --- docs/components/form.md | 67 +++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/docs/components/form.md b/docs/components/form.md index 135d04a9b06..e70fb9f6d88 100644 --- a/docs/components/form.md +++ b/docs/components/form.md @@ -4,7 +4,7 @@ title: Form # `<Form>` -The `<Form>` component is a declarative way to perform data mutations: creating, updating, and deleting data. While it might be a mind-shift to think about these tasks as "navigation", it's how the web has handled mutations since before JavaScript was created! +A progressively enhanced HTML `<form>` wrapper, useful for submissions that should also change the URL or otherwise add an entry to the browser history stack. For forms that shouldn't manipulate the browser history stack, use [`<fetcher.Form>`](../hooks/use-fetcher). ```tsx import { Form } from "@remix-run/react"; @@ -19,37 +19,13 @@ function NewEvent() { } ``` -- Whether JavaScript is on the page or not, your data interactions created with `<Form>` and `action` will work. -- After a `<Form>` submission, all the loaders on the page will be reloaded. This ensures that any updates to your data are reflected in the UI. -- `<Form>` automatically serializes your form's values (identically to the browser when not using JavaScript). -- You can build "optimistic UI" and pending indicators with [`useNavigation`][usenavigation]. - ## Props ### `action` -Most of the time you can omit this prop. Forms without an action prop (`<Form method="post">`) will automatically post to the same route within which they are rendered. This makes collocating your component, your data reads, and your data writes a snap. - -If you need to post to a different route, then add an action prop: - -```tsx -<Form action="/projects/new" method="post" /> -``` - -Because index routes and their parent route share the same URL, the `?index` param is used to differentiate between them. - -```tsx -<Form action="/accounts?index" method="post" /> -``` - -| action url | route action | -| ----------------- | -------------------------------- | -| `/accounts?index` | `app/routes/accounts._index.tsx` | -| `/accounts` | `app/routes/accounts.tsx` | +The URL to submit the form data to. -See also: - -- [`?index` query param][index query param] +If `undefined`, the action defaults to the closest route in context. If a parent route renders a `<Form>` but the URL matches deeper child routes, the form will post to the parent route. Likewise, a form inside the child route will post to the child route. This differs from a native `<form>` that will always point to the full URL. ### `method` @@ -59,29 +35,29 @@ This determines the [HTTP verb][http-verb] to be used: get, post, put, patch, de <Form method="post" /> ``` -Native `<form>` only supports get and post, so if you want your form to work with JavaScript on or off the page you'll need to stick with those two. +Native `<form>` only supports GET and POST, so you should avoid the other verbs if you'd like to support [progressive enhancement](../discussion/06-progressive-enhancement) -Without JavaScript, Remix will turn non-get requests into "post", but you'll still need to instruct your server with a hidden input like `<input type="hidden" name="_method" value="delete" />`. If you always include JavaScript, you don't need to worry about this. +### `encType` -We generally recommend sticking with "get" and "post" because the other verbs are not supported by HTML. +The encoding type to use for the form submission. -### `encType` +```tsx +<Form encType="multipart/form-data" /> +``` Defaults to `application/x-www-form-urlencoded`, use `multipart/form-data` for file uploads. ### `replace` +Replaces the current entry in the history stack, instead of pushing the new entry. + ```tsx <Form replace /> ``` -Instructs the form to replace the current entry in the history stack, instead of pushing the new entry. If you expect a form to be submitted multiple times you may not want the user to have to click "back" for every submission to get to the previous page. - -This has no effect without JavaScript on the page. - ### `reloadDocument` -If true, it will submit the form with the browser instead of JavaScript, even if JavaScript is on the page, the same as a native `<form>`. +If true, it will submit the form with the browser instead of client side routing. The same as a native `<form>`. ```tsx <Form reloadDocument /> @@ -89,6 +65,25 @@ If true, it will submit the form with the browser instead of JavaScript, even if This is recommended over `<form />`. When the `action` prop is omitted, `<Form>` and `<form>` will sometimes call different actions depending on what the current URL is since `<form>` uses the current URL as the default, but `<Form>` uses the URL for the route the form is rendered in. +## Notes + +### `?index` + +Because index routes and their parent route share the same URL, the `?index` param is used to differentiate between them. + +```tsx +<Form action="/accounts?index" method="post" /> +``` + +| action url | route action | +| ----------------- | -------------------------------- | +| `/accounts?index` | `app/routes/accounts._index.tsx` | +| `/accounts` | `app/routes/accounts.tsx` | + +See also: + +- [`?index` query param][index query param] + ## Additional Resources **Videos:** From 33ebdf91f2f06bd98a20b57051ca672c5e10d599 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Thu, 31 Aug 2023 14:40:55 -0600 Subject: [PATCH 12/46] docs: link docs --- docs/components/link.md | 135 ++++++++++++++++++++++++++++++++++------ 1 file changed, 116 insertions(+), 19 deletions(-) diff --git a/docs/components/link.md b/docs/components/link.md index 373f6d00489..bc72a1c0660 100644 --- a/docs/components/link.md +++ b/docs/components/link.md @@ -4,25 +4,17 @@ title: Link # `<Link>` -This component renders an anchor tag and is the primary way the user will navigate around your website. Anywhere you would have used `<a href="...">` you should now use `<Link to="..."/>` to get all the performance benefits of client-side routing in Remix. +A `<a href>` wrapper to enable navigation with client-side routing. ```tsx import { Link } from "@remix-run/react"; -export default function GlobalNav() { - return ( - <nav> - <Link to="/dashboard">Dashboard</Link>{" "} - <Link to="/account">Account</Link>{" "} - <Link to="/support">Support</Link> - </nav> - ); -} +<Link to="/dashboard">Dashboard</Link>; ``` ## `prefetch` -In the effort to remove all loading states from your UI, `Link` can automatically prefetch all the resources the next page needs: JavaScript modules, stylesheets, and data. This prop controls if and when that happens. +Defines the data and module prefetching behavior for the link. ```tsx <> @@ -34,17 +26,122 @@ In the effort to remove all loading states from your UI, `Link` can automaticall </> ``` -- **"none"** - Default behavior. This will prevent any prefetching from happening. This is recommended when linking to pages that require a user session that the browser won't be able to prefetch anyway. -- **"intent"** - Recommended if you want to prefetch. Fetches when Remix thinks the user intends to visit the link. Right now the behavior is simple: if they hover or focus the link it will prefetch the resources. In the future we hope to make this even smarter. Links with large click areas/padding get a bit of a head start. It is worth noting that when using `prefetch="intent"`, `<link rel="prefetch">` elements will be inserted on hover/focus and removed if the `<Link>` loses hover/focus. Without proper `cache-control` headers on your loaders, this could result in repeated prefetch loads if a user continually hovers on and off a link. -- **"render"** - Fetches when the link is rendered. -- **"viewport"** - Fetches while the link is in the viewport +- **none** - default, no prefetching +- **intent** - prefetches when the user hovers or focuses the link +- **render** - prefetches when the link renders +- **viewport** - prefetches when the link is in the viewport, very useful for mobile + +Prefetching is done with HTML `<link rel="prefetch">` tags. They are inserted after the link. + +```tsx +<nav> + <a href="..." /> + <a href="..." /> + <link rel="prefetch" /> {/* might conditionally render */} +</nav> +``` + +Because of this, if you are using `nav :last-child` you will need to use `nav :last-of-type` so the styles don't conditionally fall off your last link (and any other similar selectors). + +## `preventScrollReset` + +If you are using [`<ScrollRestoration>`](./scroll-restoration), this lets you prevent the scroll position from being reset to the top of the window when the link is clicked. + +```tsx +<Link to="?tab=one" preventScrollReset /> +``` + +This does not prevent the scroll position from being restored when the user comes back to the location with the back/forward buttons, it just prevents the reset when the user clicks the link. + +<details> +<summary>Discussion</summary> + +An example when you might want this behavior is a list of tabs that manipulate the url search params that aren't at the top of the page. You wouldn't want the scroll position to jump up to the top because it might scroll the toggled content out of the viewport! + +``` + ┌─────────────────────────┐ + │ ├──┐ + │ │ │ + │ │ │ scrolled + │ │ │ out of view + │ │ │ + │ │ ◄┘ + ┌─┴─────────────────────────┴─┐ + │ ├─┐ + │ │ │ viewport + │ ┌─────────────────────┐ │ │ + │ │ tab tab tab │ │ │ + │ ├─────────────────────┤ │ │ + │ │ │ │ │ + │ │ │ │ │ + │ │ content │ │ │ + │ │ │ │ │ + │ │ │ │ │ + │ └─────────────────────┘ │ │ + │ │◄┘ + └─────────────────────────────┘ + +``` + +</details> + +## `relative` + +Defines the relative path behavior for the link. + +```tsx +<Link to=".." />; // default: "route" +<Link relative="route" />; +<Link relative="path" />; +``` + +- **route** - default, relative to the route hierarchy so `..` will remove all URL segments of the current route pattern +- **path** - relative to the path so `..` will remove one URL segment + +## `reloadDocument` + +Will use document navigation instead of client side routing when the link is clicked, the browser will handle the transition normally (as if it were an `<a href>`). + +```tsx +<Link to="/logout" reloadDocument /> +``` + +## `replace` + +The `replace` prop will replace the current entry in the history stack instead of pushing a new one onto it. -<docs-error>You may need to use the <code>:last-of-type</code> selector instead of <code>:last-child</code> when styling child elements inside of your links</docs-error> +```tsx +<Link replace /> +``` -Remix uses the browser's cache for prefetching with HTML `<link rel="prefetch"/>` tags, which provides a lot of subtle benefits (like respecting HTTP cache headers, doing the work in browser idle time, using a different thread than your app, etc.) but the implementation might mess with your CSS since the link tags are rendered inside of your anchor tag. This means `a *:last-child {}` style selectors won't work. You'll need to change them to `a *:last-of-type {}` and you should be good. We will eventually get rid of this limitation. +``` +# with a history stack like this +A -> B -## React Router `<Link/>` +# normal link click pushes a new entry +A -> B -> C + +# but with `replace`, B is replaced by C +A -> C +``` + +## `state` + +Adds persistent client side routing state to the next location. + +```tsx +<Link to="/somewhere/else" state={{ some: "value" }} /> +``` + +The location state is accessed from the `location`. + +```ts +function SomeComp() { + const location = useLocation(); + location.state; // { some: "value" } +} +``` -This component is a wrapper around [React Router `<Link/>`][rr-link]. It has the same API except for Remix's `prefetch` addition. For more information and advanced usage, refer to the [React Router docs][rr-link]. +This state is inaccessible on the server as it is implemented on top of [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state). [rr-link]: https://reactrouter.com/en/main/components/link From cdf9638a61f35ac20dbc3630fced8907cf2a556a Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Thu, 31 Aug 2023 20:42:37 +0000 Subject: [PATCH 13/46] chore: format --- docs/components/form.md | 6 ++++-- docs/components/link.md | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/components/form.md b/docs/components/form.md index e70fb9f6d88..e1ad63f289d 100644 --- a/docs/components/form.md +++ b/docs/components/form.md @@ -4,7 +4,7 @@ title: Form # `<Form>` -A progressively enhanced HTML `<form>` wrapper, useful for submissions that should also change the URL or otherwise add an entry to the browser history stack. For forms that shouldn't manipulate the browser history stack, use [`<fetcher.Form>`](../hooks/use-fetcher). +A progressively enhanced HTML `<form>` wrapper, useful for submissions that should also change the URL or otherwise add an entry to the browser history stack. For forms that shouldn't manipulate the browser history stack, use [`<fetcher.Form>`][fetcher-form]. ```tsx import { Form } from "@remix-run/react"; @@ -35,7 +35,7 @@ This determines the [HTTP verb][http-verb] to be used: get, post, put, patch, de <Form method="post" /> ``` -Native `<form>` only supports GET and POST, so you should avoid the other verbs if you'd like to support [progressive enhancement](../discussion/06-progressive-enhancement) +Native `<form>` only supports GET and POST, so you should avoid the other verbs if you'd like to support [progressive enhancement][progressive-enhancement] ### `encType` @@ -116,3 +116,5 @@ See also: [fullstack-data-flow]: ../discussion/03-data-flow [pending-ui]: ../discussion/07-pending-ui [form-vs-fetcher]: ../discussion/10-form-vs-fetcher +[fetcher-form]: ../hooks/use-fetcher +[progressive-enhancement]: ../discussion/06-progressive-enhancement diff --git a/docs/components/link.md b/docs/components/link.md index bc72a1c0660..d8f5447174b 100644 --- a/docs/components/link.md +++ b/docs/components/link.md @@ -45,7 +45,7 @@ Because of this, if you are using `nav :last-child` you will need to use `nav :l ## `preventScrollReset` -If you are using [`<ScrollRestoration>`](./scroll-restoration), this lets you prevent the scroll position from being reset to the top of the window when the link is clicked. +If you are using [`<ScrollRestoration>`][scroll-restoration], this lets you prevent the scroll position from being reset to the top of the window when the link is clicked. ```tsx <Link to="?tab=one" preventScrollReset /> @@ -142,6 +142,8 @@ function SomeComp() { } ``` -This state is inaccessible on the server as it is implemented on top of [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state). +This state is inaccessible on the server as it is implemented on top of [`history.state`][history-state]. [rr-link]: https://reactrouter.com/en/main/components/link +[scroll-restoration]: ./scroll-restoration +[history-state]: https://developer.mozilla.org/en-US/docs/Web/API/History/state From 3a974dbc16b571fd94c0decf92ddb9aba0d260fa Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Thu, 31 Aug 2023 14:53:41 -0600 Subject: [PATCH 14/46] docs: NavLink --- docs/components/link.md | 14 +++++++----- docs/components/nav-link.md | 44 ++++++++++++++++++++----------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/docs/components/link.md b/docs/components/link.md index d8f5447174b..4410b4d811b 100644 --- a/docs/components/link.md +++ b/docs/components/link.md @@ -12,7 +12,9 @@ import { Link } from "@remix-run/react"; <Link to="/dashboard">Dashboard</Link>; ``` -## `prefetch` +## Props + +### `prefetch` Defines the data and module prefetching behavior for the link. @@ -43,7 +45,7 @@ Prefetching is done with HTML `<link rel="prefetch">` tags. They are inserted af Because of this, if you are using `nav :last-child` you will need to use `nav :last-of-type` so the styles don't conditionally fall off your last link (and any other similar selectors). -## `preventScrollReset` +### `preventScrollReset` If you are using [`<ScrollRestoration>`][scroll-restoration], this lets you prevent the scroll position from being reset to the top of the window when the link is clicked. @@ -85,7 +87,7 @@ An example when you might want this behavior is a list of tabs that manipulate t </details> -## `relative` +### `relative` Defines the relative path behavior for the link. @@ -98,7 +100,7 @@ Defines the relative path behavior for the link. - **route** - default, relative to the route hierarchy so `..` will remove all URL segments of the current route pattern - **path** - relative to the path so `..` will remove one URL segment -## `reloadDocument` +### `reloadDocument` Will use document navigation instead of client side routing when the link is clicked, the browser will handle the transition normally (as if it were an `<a href>`). @@ -106,7 +108,7 @@ Will use document navigation instead of client side routing when the link is cli <Link to="/logout" reloadDocument /> ``` -## `replace` +### `replace` The `replace` prop will replace the current entry in the history stack instead of pushing a new one onto it. @@ -125,7 +127,7 @@ A -> B -> C A -> C ``` -## `state` +### `state` Adds persistent client side routing state to the next location. diff --git a/docs/components/nav-link.md b/docs/components/nav-link.md index 840da15c33c..a16270ed75f 100644 --- a/docs/components/nav-link.md +++ b/docs/components/nav-link.md @@ -5,7 +5,7 @@ toc: false # `<NavLink>` -A `<NavLink>` is a special kind of [`<Link>`][link] that knows whether or not it is "active" or "pending". This is useful when building a navigation menu, such as a breadcrumb or a set of tabs where you'd like to show which of them is currently selected. It also provides useful context for assistive technology like screen readers. +Wraps [`<Link>`](./link) with additional props for styling active and pending states. ```tsx import { NavLink } from "@remix-run/react"; @@ -20,25 +20,31 @@ import { NavLink } from "@remix-run/react"; </NavLink>; ``` -## Default `active` class +## Automatic Attributes -By default, an `active` class is added to a `<NavLink>` component when it is active so you can use CSS to style it. +### `.active` + +An `active` class is added to a `<NavLink>` component when it is active so you can use CSS to style it. ```tsx -<nav id="sidebar"> - <NavLink to="/messages" /> -</nav> +<NavLink to="/messages" /> ``` ```css -#sidebar a.active { +a.active { color: red; } ``` -## `className` +### `aria-current` + +When a `NavLink` is active it will automatically apply `<a aria-current="page">` to the underlying anchor tag. See [aria-current][aria-current] on MDN. + +## Props -The `className` prop works like a normal className, but you can also pass it a function to customize the classNames applied based on the active and pending state of the link. +### `className` callback + +Calls back with the active and pending states to allow customizing the class names applied. ```tsx <NavLink @@ -51,9 +57,9 @@ The `className` prop works like a normal className, but you can also pass it a f </NavLink> ``` -## `style` +### `style` callback -The `style` prop works like a normal style prop, but you can also pass it a function to customize the styles applied based on the active and pending state of the link. +Calls back with the active and pending states to allow customizing the styles applied. ```tsx <NavLink @@ -69,9 +75,9 @@ The `style` prop works like a normal style prop, but you can also pass it a func </NavLink> ``` -## `children` +### `children` callback -You can pass a render prop as children to customize the content of the `<NavLink>` based on the active and pending state, which is useful to change styles on internal elements. +Calls back with the active and pending states to allow customizing the content of the `<NavLink>`. ```tsx <NavLink to="/tasks"> @@ -81,7 +87,7 @@ You can pass a render prop as children to customize the content of the `<NavLink </NavLink> ``` -## `end` +### `end` The `end` prop changes the matching logic for the `active` and `pending` states to only match to the "end" of the NavLinks's `to` path. If the URL is longer than `to`, it will no longer be considered active. @@ -92,11 +98,9 @@ The `end` prop changes the matching logic for the `active` and `pending` states | `<NavLink to="/tasks" end />` | `/tasks` | true | | `<NavLink to="/tasks" end />` | `/tasks/123` | false | -**A note on links to the root route** - `<NavLink to="/">` is an exceptional case because _every_ URL matches `/`. To avoid this matching every single route by default, it effectively ignores the `end` prop and only matches when you're at the root route. -## `caseSensitive` +### `caseSensitive` Adding the `caseSensitive` prop changes the matching logic to make it case sensitive. @@ -105,9 +109,9 @@ Adding the `caseSensitive` prop changes the matching logic to make it case sensi | `<NavLink to="/SpOnGe-bOB" />` | `/sponge-bob` | true | | `<NavLink to="/SpOnGe-bOB" caseSensitive />` | `/sponge-bob` | false | -## `aria-current` +### `<Link>` props -When a `NavLink` is active it will automatically apply `<a aria-current="page">` to the underlying anchor tag. See [aria-current][aria-current] on MDN. +All other props of [`<Link>`][link] are supported. [aria-current]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current -[link]: ./link.md +[link]: ./link From 69f49a9ca3706c6705673b5be292f4e32a1b9b24 Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Thu, 31 Aug 2023 20:56:51 +0000 Subject: [PATCH 15/46] chore: format --- docs/components/nav-link.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/components/nav-link.md b/docs/components/nav-link.md index a16270ed75f..0aeb8abb651 100644 --- a/docs/components/nav-link.md +++ b/docs/components/nav-link.md @@ -5,7 +5,7 @@ toc: false # `<NavLink>` -Wraps [`<Link>`](./link) with additional props for styling active and pending states. +Wraps [`<Link>`][link-2] with additional props for styling active and pending states. ```tsx import { NavLink } from "@remix-run/react"; @@ -115,3 +115,4 @@ All other props of [`<Link>`][link] are supported. [aria-current]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current [link]: ./link +[link-2]: ./link From 6e6b60a12dc991e9e1db2a2ac1da43553e78655e Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Thu, 31 Aug 2023 15:02:22 -0600 Subject: [PATCH 16/46] docs: outlet --- docs/components/outlet.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/components/outlet.md b/docs/components/outlet.md index d0b481392d5..c30629321be 100644 --- a/docs/components/outlet.md +++ b/docs/components/outlet.md @@ -5,6 +5,18 @@ toc: false # `<Outlet>` -<docs-info>This component is simply a re-export of [React Router's `Outlet`][rr-outlet].</docs-info> +Renders the matching child route of a parent route. -[rr-outlet]: https://reactrouter.com/components/outlet +```tsx +import { Outlet } from "@remix-run/react"; + +export function SomeParent() { + return ( + <div> + <h1>Parent Content</h1> + + <Outlet /> + </div> + ); +} +``` From 6c0310cbe968b1b41fd31dd281a2e18e8c0f00ff Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Thu, 31 Aug 2023 15:05:35 -0600 Subject: [PATCH 17/46] docs: Scripts --- docs/components/scripts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/components/scripts.md b/docs/components/scripts.md index 5cf4c7c39f0..cb26376e569 100644 --- a/docs/components/scripts.md +++ b/docs/components/scripts.md @@ -22,6 +22,6 @@ export default function Root() { } ``` -If you don't render the `<Scripts/>` component, your app will still work like a traditional web app without JavaScript, relying solely on HTML and browser behaviors. That's cool, but we personally have bigger goals than spinning favicons, so we recommend adding JavaScript to your app 😎 +If you don't render the `<Scripts/>` component, your app will still work like a traditional web app without JavaScript, relying solely on HTML and browser behaviors. [meta]: ../route/meta From 7a3667565017be77d611aa10f1f3256fc08353f8 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Fri, 1 Sep 2023 12:17:35 -0600 Subject: [PATCH 18/46] docs: scroll restoration --- docs/components/scroll-restoration.md | 88 +++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 4 deletions(-) diff --git a/docs/components/scroll-restoration.md b/docs/components/scroll-restoration.md index 42b6f3f8708..350e79bbbea 100644 --- a/docs/components/scroll-restoration.md +++ b/docs/components/scroll-restoration.md @@ -27,10 +27,90 @@ export default function Root() { } ``` -## React Router `<ScrollRestoration/>` +## Props -This is a wrapper around [React Router `<ScrollRestoration>`][rr-scrollrestoration]. Because Remix server renders your app's HTML, it can restore scroll positions before JavaScript even loads, avoiding the janky "scroll jump" typically found in SPAs. Other than that, it is identical to the React Router version. +### `getKey` -For advanced usage, see the [React Router ScrollRestoration docs][rr-scrollrestoration]. +Optional. Defines the key used to restore scroll positions. -[rr-scrollrestoration]: https://reactrouter.com/en/main/components/scroll-restoration +```tsx +<ScrollRestoration + getKey={(location, matches) => { + // default behavior + return location.key; + }} +/> +``` + +<details> +<summary>Discussion</summary> + +Using `location.key` emulates the browser's default behavior. The user can navigate to the same URL multiple times in the stack and each entry gets its own scroll position to restore. + +Some apps may want to override this behavior and restore position based on something else. Consider a social app that has four primary pages: + +- "/home" +- "/messages" +- "/notifications" +- "/search" + +If the user starts at "/home", scrolls down a bit, clicks "messages" in the navigation menu, then clicks "home" in the navigation menu (not the back button!) there will be three entries in the history stack: + +``` +1. /home +2. /messages +3. /home +``` + +By default, React Router (and the browser) will have two different scroll positions stored for `1` and `3` even though they have the same URL. That means as the user navigated from `2` → `3` the scroll position goes to the top instead of restoring to where it was in `1`. + +A solid product decision here is to keep the users scroll position on the home feed no matter how they got there (back button or new link clicks). For this, you'd want to use the `location.pathname` as the key. + +```tsx +<ScrollRestoration + getKey={(location, matches) => { + return location.pathname; + }} +/> +``` + +Or you may want to only use the pathname for some paths, and use the normal behavior for everything else: + +```tsx +<ScrollRestoration + getKey={(location, matches) => { + const paths = ["/home", "/notifications"]; + return paths.includes(location.pathname) + ? // home and notifications restore by pathname + location.pathname + : // everything else by location like the browser + location.key; + }} +/> +``` + +</details> + +### `nonce` + +`<ScrollRestoration>` renders an inline `<script>` to prevent scroll flashing. The `nonce` prop will be passed down to the script tag to allow CSP nonce usage. + +```tsx +<ScrollRestoration nonce={cspNonce} /> +``` + +## Preventing Scroll Reset + +When navigating to new locations, the scroll position is reset to the top of the page. You can prevent the "scroll to top" behavior from your links and forms: + +```tsx +<Link preventScrollReset={true} />; +<Form preventScrollReset={true} />; +``` + +See also: [`<Link preventScrollReset>`][preventscrollreset], [`<Form preventScrollReset>`][form-preventscrollreset] + +[remix]: https://remix.run +[preventscrollreset]: ../components/link#preventscrollreset +[form-preventscrollreset]: ../components/form#preventscrollreset +[pickingarouter]: ../routers/picking-a-router From 802e40eac87633996d6db5b10c82a28a95035bdd Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Fri, 1 Sep 2023 13:40:53 -0600 Subject: [PATCH 19/46] docs: useActionData - new form validation guide - new resubmission discussion --- docs/discussion/resubmissions.md | 67 ++++++++++++++ docs/guides/form-validation.md | 109 +++++++++++++++++++++++ docs/hooks/use-action-data.md | 146 +++---------------------------- 3 files changed, 190 insertions(+), 132 deletions(-) create mode 100644 docs/discussion/resubmissions.md create mode 100644 docs/guides/form-validation.md diff --git a/docs/discussion/resubmissions.md b/docs/discussion/resubmissions.md new file mode 100644 index 00000000000..f0603f91e02 --- /dev/null +++ b/docs/discussion/resubmissions.md @@ -0,0 +1,67 @@ +--- +title: Form Resubmissions +--- + +# Form Resubmissions + +When you use `<Form method="post">` in Remix, as opposed to using the native HTML `<form method="post">`, Remix does not adhere to the default browser behavior for resubmitting forms during navigation events like clicking back, forward, or refreshing. + +## The Browser's Default Behavior + +In a standard browser environment, form submissions are navigation events. This means that when a user clicks the back button, the browser will typically resubmit the form. For example: + +1. User visits `/buy` +2. Submits a form to `/checkout` +3. Navigates to `/order/123` + +The browser history stack would look like this: + +``` +GET /buy > POST /checkout > *GET /order/123 +``` + +If the user clicks the back button, the history becomes: + +``` +GET /buy - *POST /checkout < GET /order/123 +``` + +In this situation, the browser resubmits the form data, which could lead to issues such as charging a credit card twice. + +## Redirecting from Actions + +A common practice to avoid this issue is to issue a redirect after the POST request. This removes the POST action from the browser's history. The history stack would then look like this: + +``` +GET /buy > POST /checkout, Redirect > GET /order/123 +``` + +With this approach, clicking the back button would not resubmit the form: + +``` +GET /buy - *GET /order/123 +``` + +## Specific Scenarios to Consider + +While accidental resubmissions generally don't happen in Remix, there are specific scenarios where they might. + +- You used `<form>` instead of `<Form>` +- You used `<Form reloadDocument>` +- You are not rendering `<Scripts/>` +- JavaScript is disabled by the user +- JavaScript had not loaded when the form was submitted + +It's advisable to implement a redirect from the action to avoid unintended resubmissions. + +## Additional Resources + +**Guides** + +- [Form Validation](../guides/form-validation) + +**API** + +- [`<Form>`](../components/form) +- [`useActionData`](../hooks/use-action-data) +- [`redirect`](../utils/redirect) diff --git a/docs/guides/form-validation.md b/docs/guides/form-validation.md new file mode 100644 index 00000000000..a5deebf7d66 --- /dev/null +++ b/docs/guides/form-validation.md @@ -0,0 +1,109 @@ +--- +title: Form Validation +--- + +# Form Validation + +This guide walks you through implementing form validation for a simple signup form in Remix. Here, we focus on capturing the fundamentals to help you understand the essential elements of form validation in Remix, including actions, action data, and rendering errors. + +## Step 1: Setting Up the Signup Form + +We'll start by creating a basic signup form using the `Form` component from Remix. + +```tsx filename=app/routes/signup.tsx +import { Form } from "@remix-run/react"; + +export default function Signup() { + return ( + <Form method="post"> + <p> + <input type="email" name="email" /> + </p> + + <p> + <input type="password" name="password" /> + </p> + + <button type="submit">Sign Up</button> + </Form> + ); +} +``` + +## Step 2: Defining the Action + +In this step, we'll define a server action in the same file as our Signup component. Note that the aim here is to provide a broad overview of the mechanics involved rather than digging deep into form validation rules or error object structures. We'll use rudimentary checks for the email and password to demonstrate the core concepts. + +```tsx filename=app/routes/signup.jsx +import { Form, redirect } from "@remix-run/react"; + +export default function Signup() { + // omitted for brevity +} + +export function action({ request }) { + const formData = await request.formData(); + const email = String(formData.get("email")); + const password = String(formData.get("password")); + + const errors = {}; + + if (!email.includes("@")) { + errors.email = "Invalid email address"; + } + + if (password.length < 12) { + errors.password = + "Password should be at least 12 characters"; + } + + if (Object.keys(errors).length > 0) { + return errors; + } + + // Redirect to dashboard if validation is successful + return redirect("/dashboard"); +} +``` + +If any validation errors are found, they are returned from the action to the client. This is our way of signaling to the UI that something needs to be corrected, otherwise the user will be redirected to the dashboard. + +## Step 3: Displaying Validation Errors + +Finally, we'll modify the Signup component to display validation errors, if any. We'll use `useActionData` to access and display these errors. + +```tsx filename=app/routes/signup.jsx +import { + Form, + redirect, + useActionData, +} from "@remix-run/react"; + +export default function Signup() { + const errors = useActionData(); + + return ( + <Form method="post"> + <p> + <input type="email" name="email" /> + {errors?.email && <em>{errors.email}</em>} + </p> + + <p> + <input type="password" name="password" /> + {errors?.password && <em>{errors.password}</em>} + </p> + + <button type="submit">Sign Up</button> + </Form> + ); +} + +export function action({ request }) { + // omitted for brevity +} +``` + +## Conclusion + +And there you have it! You've successfully set up a basic form validation flow in Remix. The beauty of this approach is that the errors will automatically display based on the action data, and they will be updated each time the user re-submits the form. This reduces the amount of boilerplate code you have to write, making your development process more efficient. diff --git a/docs/hooks/use-action-data.md b/docs/hooks/use-action-data.md index 9d3ae3ec55c..1e2916f3c56 100644 --- a/docs/hooks/use-action-data.md +++ b/docs/hooks/use-action-data.md @@ -5,162 +5,44 @@ toc: false # `useActionData` -<docs-info>This hook is simply a re-export of [React Router's `useActionData`][rr-useactiondata].</docs-info> +Returns the serialized data from the most recent route action or undefined if there isn't one. -This hook returns the JSON parsed data from your route action. It returns `undefined` if there hasn't been a submission at the current location yet. - -```tsx lines=[3,12,21] -import type { ActionArgs } from "@remix-run/node"; // or cloudflare/deno -import { json } from "@remix-run/node"; // or cloudflare/deno +```tsx lines=[7,11] +import type { ActionArgs } from "@remix-run/node"; import { Form, useActionData } from "@remix-run/react"; export async function action({ request }: ActionArgs) { const body = await request.formData(); const name = body.get("visitorsName"); - return json({ message: `Hello, ${name}` }); + return { message: `Hello, ${name}` }; } export default function Invoices() { const data = useActionData<typeof action>(); return ( <Form method="post"> - <p> - <label> - What is your name? - <input type="text" name="visitorsName" /> - </label> - </p> - <p>{data ? data.message : "Waiting..."}</p> + <input type="text" name="visitorsName" /> + {data ? data.message : "Waiting..."} </Form> ); } ``` -The most common use-case for this hook is form validation errors. If the form isn't right, you can simply return the errors and let the user try again (instead of pushing all the errors into sessions and back out of the loader). - -```tsx lines=[23,32,40-42,46-48] -import type { ActionArgs } from "@remix-run/node"; // or cloudflare/deno -import { json, redirect } from "@remix-run/node"; // or cloudflare/deno -import { Form, useActionData } from "@remix-run/react"; - -export async function action({ request }: ActionArgs) { - const form = await request.formData(); - const email = form.get("email"); - const password = form.get("password"); - const errors = {}; - - // validate the fields - if (typeof email !== "string" || !email.includes("@")) { - errors.email = - "That doesn't look like an email address"; - } - - if (typeof password !== "string" || password.length < 6) { - errors.password = "Password must be > 6 characters"; - } - - // return data if we have errors - if (Object.keys(errors).length) { - return json(errors, { status: 422 }); - } - - // otherwise create the user and redirect - await createUser(form); - return redirect("/dashboard"); -} - -export default function Signup() { - const errors = useActionData<typeof action>(); - - return ( - <> - <h1>Signup</h1> - <Form method="post"> - <p> - <input type="text" name="email" /> - {errors?.email ? ( - <span>{errors.email}</span> - ) : null} - </p> - <p> - <input type="text" name="password" /> - {errors?.password ? ( - <span>{errors.password}</span> - ) : null} - </p> - <p> - <button type="submit">Sign up</button> - </p> - </Form> - </> - ); -} -``` - -#### Notes about resubmissions - -When using `<Form>` (instead of `<form>` or `<Form reloadDocument>`), Remix _does not_ follow the browser's behavior of resubmitting forms when the user clicks back, forward, or refreshes into the location. +## Additional Resources -<docs-info>Remix client-side navigation does not resubmit forms on pop events like browsers.</docs-info> +**Guides** -Form submissions are navigation events in browsers (and Remix), which means users can click the back button into a location that had a form submission _and the browser will resubmit the form_. You usually don't want this to happen. - -For example, consider this user flow: - -1. The user lands at `/buy` -2. They submit a form to `/checkout` -3. They click a link to `/order/123` - -The history stack looks like this, where "\*" is the current entry: - -``` -GET /buy > POST /checkout > *GET /order/123 -``` +- [Form Validation](../guides/form-validation) -Now consider the user clicks the back button 😨 - -``` -GET /buy - *POST /checkout < GET /order/123 -``` - -The browser will repost the same information and likely charge their credit card again. You usually don't want this. - -The decades-old best practice is to redirect in the POST request. This way the location disappears from the browser's history stack and the user can't "back into it" anymore. - -``` -GET /buy > POST /checkout, Redirect > GET /order/123 -``` - -This results in a history stack that looks like this: - -``` -GET /buy - *GET /order/123 -``` - -Now the user can click back without resubmitting the form. - -**When you should worry about this** - -Usually your actions will either return validation issues or redirect, and then your data and your user's are safe no matter how the form is submitted. But to go into further detail, if you're using: - -- `<form>` -- `<Form reloadDocument>` -- You're not rendering `<Scripts/>` -- The user has JavaScript disabled - -The browser will resubmit the form in these situations unless you redirect from the action. If these are cases you want to support, we recommend you follow the age-old best practice of redirecting from actions. - -If you're using `<Form>` and don't care to support the cases above, you don't need to redirect from your actions. However, if you don't redirect from an action, make sure reposting the same information isn't dangerous to your data or your visitors because you can't control if they have JavaScript enabled or not. - -<docs-info>In general, if the form validation fails, return data from the action and render it in the component. But, once you actually change data (in your database, or otherwise), you should redirect.</docs-info> - -<docs-info>For more information and usage, please refer to the [React Router `useActionData` docs][rr-useactiondata].</docs-info> - -See also: +**Related API** - [`action`][action] - [`useNavigation`][usenavigation] +**Discussions** + +- [Fullstack Data Flow](../discussion/03-data-flow) + [action]: ../route/action [usenavigation]: ../hooks/use-navigation [rr-useactiondata]: https://reactrouter.com/hooks/use-action-data From 928851620eddfbd83067dc8d973dcaf9cfa4748a Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Fri, 1 Sep 2023 19:43:01 +0000 Subject: [PATCH 20/46] chore: format --- docs/discussion/resubmissions.md | 13 +++++++++---- docs/hooks/use-action-data.md | 6 ++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/discussion/resubmissions.md b/docs/discussion/resubmissions.md index f0603f91e02..f2162e47478 100644 --- a/docs/discussion/resubmissions.md +++ b/docs/discussion/resubmissions.md @@ -58,10 +58,15 @@ It's advisable to implement a redirect from the action to avoid unintended resub **Guides** -- [Form Validation](../guides/form-validation) +- [Form Validation][form-validation] **API** -- [`<Form>`](../components/form) -- [`useActionData`](../hooks/use-action-data) -- [`redirect`](../utils/redirect) +- [`<Form>`][form] +- [`useActionData`][use-action-data] +- [`redirect`][redirect] + +[form-validation]: ../guides/form-validation +[form]: ../components/form +[use-action-data]: ../hooks/use-action-data +[redirect]: ../utils/redirect diff --git a/docs/hooks/use-action-data.md b/docs/hooks/use-action-data.md index 1e2916f3c56..a1fc6fc5414 100644 --- a/docs/hooks/use-action-data.md +++ b/docs/hooks/use-action-data.md @@ -32,7 +32,7 @@ export default function Invoices() { **Guides** -- [Form Validation](../guides/form-validation) +- [Form Validation][form-validation] **Related API** @@ -41,8 +41,10 @@ export default function Invoices() { **Discussions** -- [Fullstack Data Flow](../discussion/03-data-flow) +- [Fullstack Data Flow][fullstack-data-flow] [action]: ../route/action [usenavigation]: ../hooks/use-navigation [rr-useactiondata]: https://reactrouter.com/hooks/use-action-data +[form-validation]: ../guides/form-validation +[fullstack-data-flow]: ../discussion/03-data-flow From e7d7d8e8e465811949eb2b6d00f1516b58e1317c Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Sat, 2 Sep 2023 18:54:05 -0600 Subject: [PATCH 21/46] docs stuff --- docs/hooks/use-async-error.md | 34 +++++++++++++++++++++++++++++++--- docs/hooks/use-async-value.md | 31 ++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/docs/hooks/use-async-error.md b/docs/hooks/use-async-error.md index 8fac86db84c..90397baa8f2 100644 --- a/docs/hooks/use-async-error.md +++ b/docs/hooks/use-async-error.md @@ -1,10 +1,38 @@ --- title: useAsyncError -toc: false +new: true --- # `useAsyncError` -<docs-info>This hook is simply a re-export of [React Router's `useAsyncError`][rr-useassyncerror].</docs-info> +Returns the rejection value from the closest [`<Await>`][await] component. -[rr-useassyncerror]: https://reactrouter.com/hooks/use-async-error +```tsx [4,12] +import { useAsyncError, Await } from "react-router-dom"; + +function ErrorElement() { + const error = useAsyncError(); + return ( + <p>Uh Oh, something went wrong! {error.message}</p> + ); +} + +<Await + resolve={promiseThatRejects} + errorElement={<ErrorElement />} +/>; +``` + +## Additional Resources + +**Guides** + +- [Streaming](../guides/streaming) + +**API** + +- [`<Await/>`][await] +- [`useAsyncValue()`][use_async_value] + +[await]: ../components/await +[use_async_value]: ../hooks/use-async-value diff --git a/docs/hooks/use-async-value.md b/docs/hooks/use-async-value.md index b5fa6d8d2fa..934c5b111d6 100644 --- a/docs/hooks/use-async-value.md +++ b/docs/hooks/use-async-value.md @@ -1,10 +1,35 @@ --- title: useAsyncValue -toc: false +new: true --- # `useAsyncValue` -<docs-info>This hook is simply a re-export of [React Router's `useAsyncValue`][rr-useassyncvalue].</docs-info> +Returns the resolved data from the closest `<Await>` ancestor component. -[rr-useassyncvalue]: https://reactrouter.com/hooks/use-async-value +```tsx +function SomeDescendant() { + const value = useAsyncValue(); + // ... +} +``` + +```tsx +<Await resolve={somePromise}> + <SomeDescendant /> +</Await> +``` + +## Additional Resources + +**Guides** + +- [Streaming](../guides/streaming) + +**API** + +- [`<Await/>`][await] +- [`useAsyncError`][use_async_error] + +[await]: ../components/await +[use_async_error]: ../hooks/use-async-error From 975f78d31af957cbd020895cf190bb14060dc48d Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Sun, 3 Sep 2023 00:56:20 +0000 Subject: [PATCH 22/46] chore: format --- docs/hooks/use-async-error.md | 3 ++- docs/hooks/use-async-value.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/hooks/use-async-error.md b/docs/hooks/use-async-error.md index 90397baa8f2..100d89385ab 100644 --- a/docs/hooks/use-async-error.md +++ b/docs/hooks/use-async-error.md @@ -27,7 +27,7 @@ function ErrorElement() { **Guides** -- [Streaming](../guides/streaming) +- [Streaming][streaming] **API** @@ -36,3 +36,4 @@ function ErrorElement() { [await]: ../components/await [use_async_value]: ../hooks/use-async-value +[streaming]: ../guides/streaming diff --git a/docs/hooks/use-async-value.md b/docs/hooks/use-async-value.md index 934c5b111d6..4dfb391c8d0 100644 --- a/docs/hooks/use-async-value.md +++ b/docs/hooks/use-async-value.md @@ -24,7 +24,7 @@ function SomeDescendant() { **Guides** -- [Streaming](../guides/streaming) +- [Streaming][streaming] **API** @@ -33,3 +33,4 @@ function SomeDescendant() { [await]: ../components/await [use_async_error]: ../hooks/use-async-error +[streaming]: ../guides/streaming From 1b0cd09ff8c5c447c56d411aa21238ee025660ed Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 5 Sep 2023 11:30:25 -0600 Subject: [PATCH 23/46] docs: more API docs updates --- docs/discussion/10-form-vs-fetcher.md | 67 +++- docs/hooks/use-before-unload.md | 6 - docs/hooks/use-fetcher.md | 424 +++----------------------- 3 files changed, 105 insertions(+), 392 deletions(-) diff --git a/docs/discussion/10-form-vs-fetcher.md b/docs/discussion/10-form-vs-fetcher.md index c7a04a25a7b..2ad7fc0eceb 100644 --- a/docs/discussion/10-form-vs-fetcher.md +++ b/docs/discussion/10-form-vs-fetcher.md @@ -23,7 +23,7 @@ The primary criterion when choosing among these tools is whether you want the UR - **Expected Behavior**: In many cases, when users hit the back button, they should be taken to the previous page. Other times the history entry may be replaced but the URL change is important nonetheless. -- **No URL Change Desired**: For actions that don't significantly change the context or primary content of the current view. This might include updating individual fields or minor data manipulations that don't warrant a new URL or page reload. +- **No URL Change Desired**: For actions that don't significantly change the context or primary content of the current view. This might include updating individual fields or minor data manipulations that don't warrant a new URL or page reload. This also applies to loading data with fetchers for things like popovers, comboboxes, etc. ### Specific Use Cases @@ -47,6 +47,8 @@ These actions are generally more subtle and don't require a context switch for t - **Creating a Record in a List View**: When adding a new item to a list, it often makes sense for the user to remain in that context, seeing their new item added to the list without a full page transition. +- **Loading Data for a Popover or Combobox**: When loading data for a popover or combobox, the user's context remains unchanged. The data is loaded in the background and displayed in a small, self-contained UI element. + For such actions, `useFetcher` is the go-to API. It's versatile, combining functionalities of the other four APIs, and is perfectly suited for tasks where the URL should remain unchanged. ## API Comparison @@ -187,6 +189,69 @@ Furthermore, with each fetcher having the autonomy to manage its own state, oper In essence, useFetcher offers a seamless mechanism for actions that don't necessitate a change in the URL or navigation, enhancing the user experience by providing real-time feedback and context preservation. +### Mark Article as Read + +Imagine you want to mark that an article has been read by the current user, after they've been on the page for a while and scrolled to the bottom. You could make a hook that looks something like this: + +```tsx +function useMarkAsRead({ articleId, userId }) { + const marker = useFetcher(); + + useSpentSomeTimeHereAndScrolledToTheBottom(() => { + marker.submit( + { userId }, + { + method: "post", + action: `/article/${articleID}/mark-as-read`, + } + ); + }); +} +``` + +### User Avatar Details Popup + +Anytime you show the user avatar, you could put a hover effect that fetches data from a loader and displays it in a popup. + +```tsx filename=app/routes/user.$id.details.tsx +export async function loader({ params }: LoaderArgs) { + return json( + await fakeDb.user.find({ where: { id: params.id } }) + ); +} + +function UserAvatar({ partialUser }) { + const userDetails = useFetcher<typeof loader>(); + const [showDetails, setShowDetails] = useState(false); + + useEffect(() => { + if ( + showDetails && + userDetails.state === "idle" && + !userDetails.data + ) { + userDetails.load(`/users/${user.id}/details`); + } + }, [showDetails, userDetails]); + + return ( + <div + onMouseEnter={() => setShowDetails(true)} + onMouseLeave={() => setShowDetails(false)} + > + <img src={partialUser.profileImageUrl} /> + {showDetails ? ( + userDetails.state === "idle" && userDetails.data ? ( + <UserPopup user={userDetails.data} /> + ) : ( + <UserPopupLoading /> + ) + ) : null} + </div> + ); +} +``` + ## Conclusion Remix offers a range of tools to cater to varied developmental needs. While some functionalities might seem to overlap, each tool has been crafted with specific scenarios in mind. By understanding the intricacies and ideal applications of `<Form>`, `useSubmit`, `useNavigation`, `useActionData`, and `useFetcher`, developers can create more intuitive, responsive, and user-friendly web applications. diff --git a/docs/hooks/use-before-unload.md b/docs/hooks/use-before-unload.md index 6a0fb1d2065..a2f0ed2fb82 100644 --- a/docs/hooks/use-before-unload.md +++ b/docs/hooks/use-before-unload.md @@ -5,8 +5,6 @@ toc: false # `useBeforeUnload` -<docs-info>This hook is simply a re-export of [React Router's `useBeforeUnload`][rr-usebeforeunload].</docs-info> - This hook is just a helper around `window.onbeforeunload`. When users click links to pages they haven't visited yet, Remix loads the code-split modules for that page. If you deploy in the middle of a user's session, and you or your host removes the old files from the server (many do 😭), then Remix's requests for those modules will fail. Remix recovers by automatically reloading the browser at the new URL. This should start over from the server with the latest version of your application. Most of the time this works out great, and user doesn't even know anything happened. @@ -38,7 +36,3 @@ function SomeForm() { return <>{/*... */}</>; } ``` - -<docs-info>For more information and usage, please refer to the [React Router `useBeforeUnload` docs][rr-usebeforeunload].</docs-info> - -[rr-usebeforeunload]: https://reactrouter.com/hooks/use-before-unload diff --git a/docs/hooks/use-fetcher.md b/docs/hooks/use-fetcher.md index ad11454328e..f9508cd3a94 100644 --- a/docs/hooks/use-fetcher.md +++ b/docs/hooks/use-fetcher.md @@ -4,107 +4,20 @@ title: useFetcher # `useFetcher` -<docs-success>Watch the <a href="https://www.youtube.com/playlist?list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">📼 Remix Singles</a>: <a href="https://www.youtube.com/watch?v=vTzNpiOk668&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Concurrent Mutations w/ useFetcher</a> and <a href="https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Optimistic UI</a></docs-success> - -In HTML/HTTP, data mutations and loads are modeled with navigation: `<a href>` and `<form action>`. Both cause a navigation in the browser. The Remix equivalents are `<Link>` and `<Form>`. - -But sometimes you want to call a loader outside of navigation, or call an action (and get the routes to reload) but you don't want the URL to change. Many interactions with the server aren't navigation events. This hook lets you plug your UI into your actions and loaders without navigating. - -This is useful when you need to: - -- fetch data not associated with UI routes (popovers, dynamic forms, etc.) -- submit data to actions without navigating (shared components like a newsletter sign ups) -- handle multiple concurrent submissions in a list (typical "todo app" list where you can click multiple buttons and all be pending at the same time) -- infinite scroll containers -- and more! - -It is common for Remix newcomers to see this hook and think it is the primary way to interact with the server for data loading and updates--because it looks like what you might have done outside of Remix. If your use case can be modeled as "navigation", it's recommended you use one of the core data APIs before reaching for `useFetcher`: - -- [`useLoaderData`][useloaderdata] -- [`Form`][form] -- [`useActionData`][useactiondata] -- [`useNavigation`][usenavigation] - -If you're building a highly interactive, "app-like" user interface, you will use `useFetcher` often. +A hook for interacting with the server outside of navigation. ```tsx import { useFetcher } from "@remix-run/react"; -function SomeComponent() { +export function SomeComponent() { const fetcher = useFetcher(); - - // trigger the fetch with these - <fetcher.Form {...formOptions} />; - - useEffect(() => { - fetcher.submit(data, options); - fetcher.load(href); - }, [fetcher]); - - // build UI with these - fetcher.state; - fetcher.formMethod; - fetcher.formAction; - fetcher.formData; - fetcher.formEncType; - fetcher.data; + // ... } ``` -Notes about how it works: - -- Automatically handles cancellation of the fetch at the browser level -- When submitting with POST, PUT, PATCH, DELETE, the action is called first - - After the action completes, the loaders on the page are reloaded to capture any mutations that may have happened, automatically keeping your UI in sync with your server state -- When multiple fetchers are inflight at once, it will - - commit the freshest available data as they each land - - ensure no stale loads override fresher data, no matter which order the responses return -- Handles uncaught errors by rendering the nearest `ErrorBoundary` (just like a normal navigation from `<Link>` or `<Form>`) -- Will redirect the app if your action/loader being called returns a redirect (just like a normal navigation from `<Link>` or `<Form>`) - -## `fetcher.state` - -You can know the state of the fetcher with `fetcher.state`. It will be one of: - -- **idle** - Nothing is being fetched. -- **submitting** - A form has been submitted. If the method is GET, then the route loader is being called. If POST, PUT, PATCH, or DELETE, then the route action is being called. -- **loading** - The loaders for the routes are being reloaded after an action submission. - -## `fetcher.type` - -<docs-warning>`fetcher.type` will be removed in v2. For instructions on preparing for this change see the [v2 guide][v2guide].</docs-warning> - -This is the type of state the fetcher is in. It's like `fetcher.state`, but more granular. Depending on the fetcher's state, the types can be the following: - -- `state === "idle"` - - - **init** - The fetcher isn't doing anything currently and hasn't done anything yet. - - **done** - The fetcher isn't doing anything currently, but it has completed a fetch and you can safely read the `fetcher.data`. - -- `state === "submitting"` - - - **actionSubmission** - A form has been submitted with POST, PUT, PATCH, or DELETE, and the action is being called. - - **loaderSubmission** - A form has been submitted with GET and the loader is being called. - -- `state === "loading"` - - - **actionReload** - The action from an "actionSubmission" returned data and the loaders on the page are being reloaded. - - **actionRedirect** - The action from an "actionSubmission" returned a redirect and the page is navigating to the new location. - - **normalLoad** - A route's loader is being called without a submission (`fetcher.load()`). - -## `fetcher.submission` - -<docs-warning>`fetcher.submission` will be flattened into the fetcher object itself in v2. For instructions on preparing for this change see the [v2 guide][v2guide].</docs-warning> - -When using `<fetcher.Form>` or `fetcher.submit()`, the form submission is available to build optimistic UI. - -It is not available when the fetcher state is "idle" or "loading". +## Components -## `fetcher.data` - -The returned response data from your loader or action is stored here. Once the data is set, it persists on the fetcher even through reloads and resubmissions (like calling `fetcher.load()` again after having already read the data). - -## `fetcher.Form` +### `fetcher.Form` Just like `<Form>` except it doesn't cause a navigation. @@ -119,323 +32,64 @@ function SomeComponent() { } ``` -## `fetcher.submit()` - -Just like `useSubmit` except it doesn't cause a navigation. +## Methods -```tsx -function SomeComponent() { - const fetcher = useFetcher(); +### `fetcher.submit(formData, options)` - const onClick = () => - fetcher.submit({ some: "values" }, { method: "post" }); +Submits form data to a route. While multiple nested routes can match a URL, only the leaf route will be called. - // ... -} -``` +The `formData` can be multiple types: -Although a URL matches multiple Routes in a remix router hierarchy, a `fetcher.submit()` call will only call the action on the deepest matching route, unless the deepest matching route is an "index route". In this case, it will post to the parent route of the index route (because they share the same URL). +- `FormData` - A `FormData` instance. +- `HTMLFormElement` - A `<form>` DOM element. +- `Object` - An object of key/value pairs that will be converted to a `FormData` instance. -If you want to submit to an index route use `?index` in the URL: +If the method is GET, then the route loader is being called and with the formData serialized to the url as URLSearchParams. If POST, PUT, PATCH, or DELETE, then the route action is being called with FormData as the body. ```tsx +fetcher.submit(event.currentTarget.form, { + method: "POST", +}); + fetcher.submit( - { some: "values" }, - { method: "post", action: "/accounts?index" } + { serialized: "values" }, + { method: "POST" } ); -``` - -See also: - -- [`?index` query param][index query param] - -## `fetcher.load()` - -Loads data from a route loader. -```tsx -function SomeComponent() { - const fetcher = useFetcher(); - - useEffect(() => { - if (fetcher.state === "idle" && fetcher.data == null) { - fetcher.load("/some/route"); - } - }, [fetcher]); - - fetcher.data; // the data from the loader -} +fetcher.submit(formData); ``` -Although a URL matches multiple Routes in a remix router hierarchy, a `fetcher.load()` call will only call the loader on the deepest matching route, unless the deepest matching route is an "index route". In this case, it will load the parent route of the index route (because they share the same URL). +## `fetcher.load(href)` -If you want to load an index route use `?index` in the URL: +Loads data from a route loader. While multiple nested routes can match a URL, only the leaf route will be called. ```ts -fetcher.load("/some/route?index"); -``` - -See also: - -- [`?index` query param][index query param] - -## Examples - -<docs-success>Watch the <a href="https://www.youtube.com/playlist?list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">📼 Remix Single</a>: <a href="https://www.youtube.com/watch?v=jd_bin5HPrw&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Remix Newsletter Signup Form</a></docs-success> - -**Newsletter Signup Form** - -Perhaps you have a persistent newsletter signup at the bottom of every page on your site. This is not a navigation event, so `useFetcher` is perfect for the job. First, you create a Resource Route: - -```tsx filename=app/routes/newsletter.subscribe.tsx -export async function action({ request }: ActionArgs) { - const email = (await request.formData()).get("email"); - try { - await subscribe(email); - return json({ error: null, ok: true }); - } catch (error) { - return json({ error: error.message, ok: false }); - } -} +fetcher.load("/some/route"); +fetcher.load("/some/route?foo=bar"); ``` -Then, somewhere else in your app (your root layout in this example), you render the following component: - -```tsx filename=app/routes/root.tsx -// ... +## Properties -function NewsletterSignup() { - const newsletter = useFetcher(); - const ref = useRef(); +### `fetcher.state` - useEffect(() => { - if ( - newsletter.state === "idle" && - newsletter.data?.ok - ) { - ref.current.reset(); - } - }, [newsletter]); - - return ( - <newsletter.Form - ref={ref} - method="post" - action="/newsletter/subscribe" - > - <p> - <input type="text" name="email" />{" "} - <button - type="submit" - disabled={newsletter.state === "submitting"} - > - Subscribe - </button> - </p> - - {newsletter.state === "idle" && newsletter.data ? ( - newsletter.data.ok ? ( - <p>Thanks for subscribing!</p> - ) : newsletter.data.error ? ( - <p data-error>{newsletter.data.error}</p> - ) : null - ) : null} - </newsletter.Form> - ); -} -``` - -<docs-info>You can still provide a no-JavaScript experience</docs-info> - -Because `useFetcher` doesn't cause a navigation, it won't automatically work if there is no JavaScript on the page like a normal Remix `<Form>` will, because the browser will still navigate to the form's action. - -If you want to support a no JavaScript experience, just export a component from the route with the action. - -```tsx filename=app/routes/newsletter.subscribe.tsx -export async function action({ request }: ActionArgs) { - // just like before -} - -export default function NewsletterSignupRoute() { - const newsletter = useActionData<typeof action>(); - return ( - <Form method="post" action="/newsletter/subscribe"> - <p> - <input type="text" name="email" />{" "} - <button type="submit">Subscribe</button> - </p> - - {newsletter.data.ok ? ( - <p>Thanks for subscribing!</p> - ) : newsletter.data.error ? ( - <p data-error>{newsletter.data.error}</p> - ) : null} - </Form> - ); -} -``` - -- When JS is on the page, the user will subscribe to the newsletter and the page won't change, they'll just get a solid, dynamic experience. -- When JS is not on the page, they'll be navigated to the signup page by the browser. - -You could even refactor the component to take props from the hooks and reuse it: - -```tsx filename=app/routes/newsletter.subscribe.tsx -import { Form, useFetcher } from "@remix-run/react"; - -// used in the footer -export function NewsletterSignup() { - const newsletter = useFetcher(); - return ( - <NewsletterForm - Form={newsletter.Form} - data={newsletter.data} - state={newsletter.state} - /> - ); -} - -// used here and in the route -export function NewsletterForm({ Form, data, state }) { - // refactor a bit in here, just read from props instead of useFetcher -} -``` - -And now you could reuse the same form, but it gets data from a different hook for the no-js experience: - -```tsx filename=app/routes/newsletter.subscribe.tsx -import { Form } from "@remix-run/react"; - -import { NewsletterForm } from "~/NewsletterSignup"; - -export default function NewsletterSignupRoute() { - const data = useActionData<typeof action>(); - return ( - <NewsletterForm Form={Form} data={data} state="idle" /> - ); -} -``` - -**Mark Article as Read** - -Imagine you want to mark that an article has been read by the current user, after they've been on the page for a while and scrolled to the bottom. You could make a hook that looks something like this: - -```tsx -function useMarkAsRead({ articleId, userId }) { - const marker = useFetcher(); - - useSpentSomeTimeHereAndScrolledToTheBottom(() => { - marker.submit( - { userId }, - { - method: "post", - action: `/article/${articleID}/mark-as-read`, - } - ); - }); -} -``` - -**User Avatar Details Popup** - -Anytime you show the user avatar, you could put a hover effect that fetches data from a loader and displays it in a popup. - -```tsx filename=app/routes/user.$id.details.tsx -export async function loader({ params }: LoaderArgs) { - return json( - await fakeDb.user.find({ where: { id: params.id } }) - ); -} - -function UserAvatar({ partialUser }) { - const userDetails = useFetcher<typeof loader>(); - const [showDetails, setShowDetails] = useState(false); - - useEffect(() => { - if ( - showDetails && - userDetails.state === "idle" && - !userDetails.data - ) { - userDetails.load(`/users/${user.id}/details`); - } - }, [showDetails, userDetails]); +You can know the state of the fetcher with `fetcher.state`. It will be one of: - return ( - <div - onMouseEnter={() => setShowDetails(true)} - onMouseLeave={() => setShowDetails(false)} - > - <img src={partialUser.profileImageUrl} /> - {showDetails ? ( - userDetails.state === "idle" && userDetails.data ? ( - <UserPopup user={userDetails.data} /> - ) : ( - <UserPopupLoading /> - ) - ) : null} - </div> - ); -} -``` +- **idle** - Nothing is being fetched. +- **submitting** - A form has been submitted. If the method is GET, then the route loader is being called. If POST, PUT, PATCH, or DELETE, then the route action is being called. +- **loading** - The loaders for the routes are being reloaded after an action submission. -**Async Reach UI Combobox** +### `fetcher.data` -If the user needs to select a city, you could have a loader that returns a list of cities based on a query and plug it into a Reach UI combobox: +The returned response data from your loader or action is stored here. Once the data is set, it persists on the fetcher even through reloads and resubmissions (like calling `fetcher.load()` again after having already read the data). -```tsx filename=app/routes/city-search.tsx -export async function loader({ request }: LoaderArgs) { - const url = new URL(request.url); - return json( - await searchCities(url.searchParams.get("city-query")) - ); -} +## Additional Resources -function CitySearchCombobox() { - const cities = useFetcher<typeof loader>(); +**Discussions** - return ( - <cities.Form method="get" action="/city-search"> - <Combobox aria-label="Cities"> - <div> - <ComboboxInput - name="city-query" - onChange={(event) => - cities.submit(event.target.form) - } - /> - {cities.state === "submitting" ? ( - <Spinner /> - ) : null} - </div> +- [Form vs. Fetcher](../discussion/10-form-vs-fetcher) +- [Network Concurrency Management](../discussion/09-concurrency) - {cities.data ? ( - <ComboboxPopover className="shadow-popup"> - {cities.data.error ? ( - <p>Failed to load cities :(</p> - ) : cities.data.length ? ( - <ComboboxList> - {cities.data.map((city) => ( - <ComboboxOption - key={city.id} - value={city.name} - /> - ))} - </ComboboxList> - ) : ( - <span>No results found</span> - )} - </ComboboxPopover> - ) : null} - </Combobox> - </cities.Form> - ); -} -``` +**Videos** -[form]: ../components/form -[index query param]: ../guides/routing#what-is-the-index-query-param -[usenavigation]: ./use-navigation -[useactiondata]: ./use-action-data -[useloaderdata]: ./use-loader-data -[v2guide]: ../pages/v2#usefetcher +- [Concurrent Mutations w/ useFetcher](https://www.youtube.com/watch?v=vTzNpiOk668&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6) +- [Optimistic UI](https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6) From 0ffd324e951f1d444695ed7f2b4b06ee5d834896 Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Tue, 5 Sep 2023 17:32:23 +0000 Subject: [PATCH 24/46] chore: format --- docs/hooks/use-fetcher.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/hooks/use-fetcher.md b/docs/hooks/use-fetcher.md index f9508cd3a94..9b76a6e23ea 100644 --- a/docs/hooks/use-fetcher.md +++ b/docs/hooks/use-fetcher.md @@ -86,10 +86,15 @@ The returned response data from your loader or action is stored here. Once the d **Discussions** -- [Form vs. Fetcher](../discussion/10-form-vs-fetcher) -- [Network Concurrency Management](../discussion/09-concurrency) +- [Form vs. Fetcher][form-vs-fetcher] +- [Network Concurrency Management][network-concurrency-management] **Videos** -- [Concurrent Mutations w/ useFetcher](https://www.youtube.com/watch?v=vTzNpiOk668&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6) -- [Optimistic UI](https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6) +- [Concurrent Mutations w/ useFetcher][concurrent-mutations-w-use-fetcher] +- [Optimistic UI][optimistic-ui] + +[form-vs-fetcher]: ../discussion/10-form-vs-fetcher +[network-concurrency-management]: ../discussion/09-concurrency +[concurrent-mutations-w-use-fetcher]: https://www.youtube.com/watch?v=vTzNpiOk668&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 +[optimistic-ui]: https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 From 3e426fde826044f20df7d8b78775785eed351f01 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 5 Sep 2023 11:48:29 -0600 Subject: [PATCH 25/46] docs: useFetchers --- docs/hooks/use-fetcher.md | 12 ++++ docs/hooks/use-fetchers.md | 126 +++++-------------------------------- 2 files changed, 26 insertions(+), 112 deletions(-) diff --git a/docs/hooks/use-fetcher.md b/docs/hooks/use-fetcher.md index 9b76a6e23ea..6e4d1a9da3b 100644 --- a/docs/hooks/use-fetcher.md +++ b/docs/hooks/use-fetcher.md @@ -82,6 +82,18 @@ You can know the state of the fetcher with `fetcher.state`. It will be one of: The returned response data from your loader or action is stored here. Once the data is set, it persists on the fetcher even through reloads and resubmissions (like calling `fetcher.load()` again after having already read the data). +### `fetcher.formData` + +The `FormData` instance that was submitted to the server is stored here. This is useful for optimistic UIs. + +### `fetcher.formAction` + +The URL of the submission. + +### `fetcher.formMethod` + +The form method of the submission. + ## Additional Resources **Discussions** diff --git a/docs/hooks/use-fetchers.md b/docs/hooks/use-fetchers.md index dc79ab35fb7..6b1d2066994 100644 --- a/docs/hooks/use-fetchers.md +++ b/docs/hooks/use-fetchers.md @@ -5,126 +5,28 @@ toc: false # `useFetchers` -Returns an array of all inflight fetchers. - -This is useful for components throughout the app that didn't create the fetchers but want to use their submissions to participate in optimistic UI. - -For example, imagine a UI where the sidebar lists projects, and the main view displays a list of checkboxes for the current project. The sidebar could display the number of completed and total tasks for each project. - -``` -+-----------------+----------------------------+ -| | | -| Soccer (8/9) | [x] Do the dishes | -| | | -| > Home (2/4) | [x] Fold laundry | -| | | -| | [ ] Replace battery in the | -| | smoke alarm | -| | | -| | [ ] Change lights in kids | -| | bathroom | -| | | -+-----------------+----------------------------┘ -``` - -When the user clicks a checkbox, the submission goes to the action to change the state of the task. Instead of creating a "loading state" we want to create an "optimistic UI" that will **immediately** update the checkbox to appear checked even though the server hasn't processed it yet. In the checkbox component, we can use `fetcher.formData`: +Returns an array of all inflight fetchers. This is useful for components throughout the app that didn't create the fetchers but want to use their submissions to participate in optimistic UI. ```tsx -function Task({ task }) { - const toggle = useFetcher(); - const checked = toggle.formData - ? // use the optimistic version - Boolean(toggle.formData.get("complete")) - : // use the normal version - task.complete; +import { useFetchers } from "@remix-run/react"; - const { projectId, id } = task; - return ( - <toggle.Form - method="put" - action={`/project/${projectId}/tasks/${id}`} - > - <label> - <input - type="checkbox" - checked={checked} - onChange={(e) => toggle.submit(e.target.form)} - /> - </label> - </toggle.Form> - ); +function SomeComponent() { + const fetchers = useFetchers(); + fetchers[0].formData; // FormData + fetchers[0].state; // etc. + // ... } ``` -This is awesome for the checkbox, but the sidebar will say 2/4 while the checkboxes show 3/4 when the user clicks one of them! - -``` -+-----------------+----------------------------+ -| | | -| Soccer (8/9) | [x] Do the dishes | -| | | -| > Home (2/4) | [x] Fold laundry | -| | | -| CLICK!-->[x] Replace battery in the | -| | smoke alarm | -| | | -| | [ ] Change lights in kids | -| | bathroom | -| | | -+-----------------+----------------------------┘ -``` - -Because Remix will automatically reload the routes, the sidebar will quickly update and be correct. But for a moment, it's going to feel a little funny. - -This is where `useFetchers` comes in. Up in the sidebar, we can access all the inflight fetcher states from the checkboxes - even though it's not the component that created them. +The fetchers don't contain `fetcher.Form`, `fetcher.submit`, or `fetcher.load`, only the states like `fetcher.formData`, `fetcher.state`, etc. -The strategy has three steps: +## Additional Resources -1. Find the submissions for tasks in a specific project -2. Use the `fetcher.formData` to immediately update the count -3. Use the normal task's state if it's not inflight +**Discussions** -Here's some sample code: +- [Form vs. Fetcher](../discussion/10-form-vs-fetcher) +- [Pending, Optimistic UI](../discussion/07-pending-ui) -```tsx -function ProjectTaskCount({ project }) { - const fetchers = useFetchers(); - let completedTasks = 0; +**API** - // 1) Find my task's submissions - const myFetchers = new Map(); - for (const f of fetchers) { - if ( - f.formAction && - f.formAction.startsWith( - `/projects/${project.id}/task` - ) - ) { - const taskId = f.formData.get("id"); - myFetchers.set( - parseInt(taskId), - f.formData.get("complete") === "on" - ); - } - } - - for (const task of project.tasks) { - // 2) use the optimistic version - if (myFetchers.has(task.id)) { - if (myFetchers.get(task.id)) { - completedTasks++; - } - } - // 3) use the normal version - else if (task.complete) { - completedTasks++; - } - } - - return ( - <small> - {completedTasks}/{project.tasks.length} - </small> - ); -} -``` +- [`useFetcher`](./use-fetcher) From 03e0c1fc5e0779f665084a9a7ff8d1f143389942 Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Tue, 5 Sep 2023 17:50:23 +0000 Subject: [PATCH 26/46] chore: format --- docs/hooks/use-fetchers.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/hooks/use-fetchers.md b/docs/hooks/use-fetchers.md index 6b1d2066994..bae977c2c4d 100644 --- a/docs/hooks/use-fetchers.md +++ b/docs/hooks/use-fetchers.md @@ -24,9 +24,13 @@ The fetchers don't contain `fetcher.Form`, `fetcher.submit`, or `fetcher.load`, **Discussions** -- [Form vs. Fetcher](../discussion/10-form-vs-fetcher) -- [Pending, Optimistic UI](../discussion/07-pending-ui) +- [Form vs. Fetcher][form-vs-fetcher] +- [Pending, Optimistic UI][pending-optimistic-ui] **API** -- [`useFetcher`](./use-fetcher) +- [`useFetcher`][use-fetcher] + +[form-vs-fetcher]: ../discussion/10-form-vs-fetcher +[pending-optimistic-ui]: ../discussion/07-pending-ui +[use-fetcher]: ./use-fetcher From 8a6b2f6136302ec665dae930126c1af83fb6cac3 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 5 Sep 2023 12:13:32 -0600 Subject: [PATCH 27/46] docs: useFormAction --- docs/hooks/use-form-action.md | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/hooks/use-form-action.md b/docs/hooks/use-form-action.md index 3f3c9e59e86..421fdcfcb83 100644 --- a/docs/hooks/use-form-action.md +++ b/docs/hooks/use-form-action.md @@ -4,25 +4,34 @@ title: useFormAction # `useFormAction` -<docs-info>This hook is simply a re-export of [React Router's `useFormAction`][rr-useformaction].</docs-info> +Resolves the URL to the closest route in the component hierarchy instead of the current URL of the app. -Resolves the value of a `<form action>` attribute using React Router's relative paths. This can be useful when computing the correct action for a `<button formAction>`, for example, when a `<button>` changes the action of its `<form>`. +This is used internally by `<Form>` to resolve the action to the closest route, but can be used generically as well. ```tsx +import { useFormAction } from "@remix-run/react"; function SomeComponent() { - return ( - <button - formAction={useFormAction("destroy")} - formMethod="post" - > - Delete - </button> - ); + // closest route URL + const action = useFormAction(); + + // closest route URL + "destroy" + const destroyAction = useFormAction("destroy"); } ``` -(Yes, HTML buttons can change the action of their form!) +## Signature + +``` +useFormAction(action, options) +``` + +### `action` + +Optional. The action to append to the closest route URL. + +### `options` -<docs-info>For more information and usage, please refer to the [React Router `useFormAction` docs][rr-useformaction].</docs-info> +The only option is `{ relative: "route" | "path"}`. -[rr-useformaction]: https://reactrouter.com/hooks/use-form-action +- **route** default - relative to the route hierarchy, not the URL +- **path** - makes the action relative to the URL paths, so `..` will remove one URL segment. From 8b09418464708409165fa28851ab187049a51f9e Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 5 Sep 2023 12:46:25 -0600 Subject: [PATCH 28/46] docs: useHref --- docs/hooks/use-href.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/hooks/use-href.md b/docs/hooks/use-href.md index f908bcd08bd..d0d79328c6b 100644 --- a/docs/hooks/use-href.md +++ b/docs/hooks/use-href.md @@ -5,6 +5,29 @@ toc: false # `useHref` -<docs-info>This hook is simply a re-export of [React Router's `useHref`][rr-usehref].</docs-info> +Resolves a full URL to be used as an href to a link. If a relative path is supplied, it will resolve to a full URL. -[rr-usehref]: https://reactrouter.com/hooks/use-href +```tsx +import { useHref } from "@remix-run/react"; +function SomeComponent() { + const href = useHref(); + // ... +} +``` + +## Signature + +``` +useHref(to, options) +``` + +### `to` + +Optional. The path to append to the resolved URL. + +### `options` + +The only option is `{ relative: "route" | "path"}`, which defines the behavior when resolving relative URLs. + +- **route** default - relative to the route hierarchy, not the URL +- **path** - makes the action relative to the URL paths, so `..` will remove one URL segment. From 1e5d9626cfdc85ca84d084c647e4c8c48d9a8e8f Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 5 Sep 2023 13:06:04 -0600 Subject: [PATCH 29/46] docs: useLoaderData --- docs/hooks/use-loader-data.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/hooks/use-loader-data.md b/docs/hooks/use-loader-data.md index 8b22b77d973..845e6d412bf 100644 --- a/docs/hooks/use-loader-data.md +++ b/docs/hooks/use-loader-data.md @@ -5,18 +5,13 @@ toc: false # `useLoaderData` -<docs-info>This hook is simply a re-export of [React Router's `useLoaderData`][rr-useloaderdata].</docs-info> +Returns the serialized data from the closest route loader. -<docs-success>Watch the <a href="https://www.youtube.com/playlist?list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">📼 Remix Single</a>: <a href="https://www.youtube.com/watch?v=NXqEP_PsPNc&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6">Loading data into components</a></docs-success> - -This hook returns the JSON parsed data from your route loader function. - -```tsx lines=[2,9] -import { json } from "@remix-run/node"; // or cloudflare/deno +```tsx lines=[1,8] import { useLoaderData } from "@remix-run/react"; export async function loader() { - return json(await fakeDb.invoices.findAll()); + return fakeDb.invoices.findAll(); } export default function Invoices() { @@ -25,6 +20,14 @@ export default function Invoices() { } ``` -<docs-info>For more information and usage, please refer to the [React Router `useLoaderData` docs][rr-useloaderdata].</docs-info> +## Additional Resources + +**Discussions** + +- [Fullstack Data Flow](../discussion/03-data-flow) +- [State Management](../discussion/08-state-management) + +**API** -[rr-useloaderdata]: https://reactrouter.com/hooks/use-loader-data +- [`loader`](../route/loader) +- [`useFetcher`](./use-fetcher) From aa6c458745808047d4444a160c9ec489f8a5f80e Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Tue, 5 Sep 2023 19:07:33 +0000 Subject: [PATCH 30/46] chore: format --- docs/hooks/use-loader-data.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/hooks/use-loader-data.md b/docs/hooks/use-loader-data.md index 845e6d412bf..cb21ac95fee 100644 --- a/docs/hooks/use-loader-data.md +++ b/docs/hooks/use-loader-data.md @@ -24,10 +24,15 @@ export default function Invoices() { **Discussions** -- [Fullstack Data Flow](../discussion/03-data-flow) -- [State Management](../discussion/08-state-management) +- [Fullstack Data Flow][fullstack-data-flow] +- [State Management][state-management] **API** -- [`loader`](../route/loader) -- [`useFetcher`](./use-fetcher) +- [`loader`][loader] +- [`useFetcher`][use-fetcher] + +[fullstack-data-flow]: ../discussion/03-data-flow +[state-management]: ../discussion/08-state-management +[loader]: ../route/loader +[use-fetcher]: ./use-fetcher From 013a87ece674920e3b82ccbba6e8969fb38631e6 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Tue, 5 Sep 2023 13:48:25 -0600 Subject: [PATCH 31/46] docs: useLocation --- docs/hooks/use-location.md | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/hooks/use-location.md b/docs/hooks/use-location.md index 72c4be16d05..f55c8cdceb6 100644 --- a/docs/hooks/use-location.md +++ b/docs/hooks/use-location.md @@ -5,6 +5,35 @@ toc: false # `useLocation` -<docs-info>This hook is simply a re-export of [React Router's `useLocation`][rr-uselocation].</docs-info> +Returns the current location object. -[rr-uselocation]: https://reactrouter.com/hooks/use-location +```tsx +import { useLocation } from "@remix-run/react"; + +function SomeComponent() { + const location = useLocation(); + // ... +} +``` + +## Properties + +### `location.hash` + +The hash of the current URL. + +### `location.key` + +The unique key of this location. + +### `location.pathname` + +The path of the current URL. + +### `location.search` + +The query string of the current URL. + +### `location.state` + +The state value of the location created by [`<Link state>`](../components/link) or [`navigate`](./use-navigate). From 481f73ecf7c099b450dc7f096e3b4f6361b0f8bf Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Tue, 5 Sep 2023 19:50:08 +0000 Subject: [PATCH 32/46] chore: format --- docs/hooks/use-location.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/hooks/use-location.md b/docs/hooks/use-location.md index f55c8cdceb6..8a0f770582f 100644 --- a/docs/hooks/use-location.md +++ b/docs/hooks/use-location.md @@ -36,4 +36,7 @@ The query string of the current URL. ### `location.state` -The state value of the location created by [`<Link state>`](../components/link) or [`navigate`](./use-navigate). +The state value of the location created by [`<Link state>`][link-state] or [`navigate`][navigate]. + +[link-state]: ../components/link +[navigate]: ./use-navigate From 1a570730d47bd2da1e9068ce984c1694f37eedba Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Wed, 6 Sep 2023 12:28:15 -0400 Subject: [PATCH 33/46] Revert back to node 20.5.1 for node CI runs. This resolves https://github.com/shelljs/shelljs/issues/1133 --- .github/workflows/test-pr-ubuntu.yml | 4 ++-- .github/workflows/test-pr-windows-macos.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-pr-ubuntu.yml b/.github/workflows/test-pr-ubuntu.yml index 2e7b86eef3e..4cecb5d0ab9 100644 --- a/.github/workflows/test-pr-ubuntu.yml +++ b/.github/workflows/test-pr-ubuntu.yml @@ -26,7 +26,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "ubuntu-latest" - node_version: '["latest"]' + node_version: '["20.5.1"]' integration-chromium: name: "👀 Integration Test" @@ -34,5 +34,5 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "ubuntu-latest" - node_version: '["latest"]' + node_version: '["20.5.1"]' browser: '["chromium"]' diff --git a/.github/workflows/test-pr-windows-macos.yml b/.github/workflows/test-pr-windows-macos.yml index d0affe8f70e..6115528bf98 100644 --- a/.github/workflows/test-pr-windows-macos.yml +++ b/.github/workflows/test-pr-windows-macos.yml @@ -20,7 +20,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "windows-latest" - node_version: '["latest"]' + node_version: '["20.5.1"]' integration-firefox: name: "👀 Integration Test" @@ -28,7 +28,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "ubuntu-latest" - node_version: '["latest"]' + node_version: '["20.5.1"]' browser: '["firefox"]' integration-msedge: @@ -37,7 +37,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "windows-latest" - node_version: '["latest"]' + node_version: '["20.5.1"]' browser: '["msedge"]' integration-webkit: @@ -46,5 +46,5 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "macos-latest" - node_version: '["latest"]' + node_version: '["20.5.1"]' browser: '["webkit"]' From 88d619c83146ffa1fe672cdabb5690d828074ca9 Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Thu, 7 Sep 2023 14:42:26 -0400 Subject: [PATCH 34/46] Lock in full branch runs to node 20.5 --- .github/workflows/test-full.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-full.yml b/.github/workflows/test-full.yml index 9eb2c9f9bad..cc6bbf21586 100644 --- a/.github/workflows/test-full.yml +++ b/.github/workflows/test-full.yml @@ -29,7 +29,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "ubuntu-latest" - node_version: "[18, 20]" + node_version: "['18', '20.5']" unit-windows: name: "🧪 Unit Test" @@ -37,7 +37,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "windows-latest" - node_version: "[18, 20]" + node_version: "['18', '20.5']" integration-ubuntu: name: "👀 Integration Test" @@ -45,7 +45,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "ubuntu-latest" - node_version: "[18, 20]" + node_version: "['18', '20.5']" browser: '["chromium", "firefox"]' integration-windows: @@ -54,7 +54,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "windows-latest" - node_version: "[18, 20]" + node_version: "['18', '20.5']" browser: '["msedge"]' integration-macos: @@ -63,5 +63,5 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "macos-latest" - node_version: "[18, 20]" + node_version: "['18', '20.5']" browser: '["webkit"]' From 1fac2386e440fc8d7130d03a6c00aa80e1d94802 Mon Sep 17 00:00:00 2001 From: Mark Dalgleish <mark.john.dalgleish@gmail.com> Date: Fri, 8 Sep 2023 09:11:49 +1000 Subject: [PATCH 35/46] build(deno): speed up Rollup copy step (#7364) --- packages/remix-deno/.gitignore | 2 -- packages/remix-deno/rollup.config.js | 11 ++++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 packages/remix-deno/.gitignore diff --git a/packages/remix-deno/.gitignore b/packages/remix-deno/.gitignore deleted file mode 100644 index 43b0a725e54..00000000000 --- a/packages/remix-deno/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# This file is needed for the Rollup copy plugin to ignore local node_modules/ -node_modules \ No newline at end of file diff --git a/packages/remix-deno/rollup.config.js b/packages/remix-deno/rollup.config.js index cdb2d4f611a..a434ac096fa 100644 --- a/packages/remix-deno/rollup.config.js +++ b/packages/remix-deno/rollup.config.js @@ -15,10 +15,15 @@ module.exports = function rollup() { copy({ targets: [ { src: "LICENSE.md", dest: [outputDir, sourceDir] }, - { src: `${sourceDir}/**/*`, dest: outputDir }, - { src: `!${sourceDir}/rollup.config.js`, dest: outputDir }, + { + src: [ + `${sourceDir}/**/*`, + `!${sourceDir}/rollup.config.js`, + `!${sourceDir}/node_modules`, + ], + dest: outputDir, + }, ], - gitignore: true, }), copyToPlaygrounds(), ], From 70cac93c1228f3c34101708d9c239557fb7aaa71 Mon Sep 17 00:00:00 2001 From: Leo Singer <leo.p.singer@nasa.gov> Date: Fri, 8 Sep 2023 10:45:02 -0400 Subject: [PATCH 36/46] fix(remix-dev): allow `serverBuildPath` to end with `.cjs` (#7180) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michaël De Boey <info@michaeldeboey.be> --- .changeset/wild-steaks-bathe.md | 7 +++ integration/compiler-mjs-cjs-output-test.ts | 35 +++++++++++ integration/compiler-mjs-output-test.ts | 69 --------------------- packages/remix-dev/compiler/server/write.ts | 2 +- 4 files changed, 43 insertions(+), 70 deletions(-) create mode 100644 .changeset/wild-steaks-bathe.md create mode 100644 integration/compiler-mjs-cjs-output-test.ts delete mode 100644 integration/compiler-mjs-output-test.ts diff --git a/.changeset/wild-steaks-bathe.md b/.changeset/wild-steaks-bathe.md new file mode 100644 index 00000000000..25a35dce28b --- /dev/null +++ b/.changeset/wild-steaks-bathe.md @@ -0,0 +1,7 @@ +--- +"@remix-run/dev": patch +--- + +Fix server builds where serverBuildPath extension is `.cjs`. + +Fix a bug that caused the server build file to be emitted into the assets directory if the value of `serverBuildPath` ended in `.cjs`. diff --git a/integration/compiler-mjs-cjs-output-test.ts b/integration/compiler-mjs-cjs-output-test.ts new file mode 100644 index 00000000000..86b03edd7a3 --- /dev/null +++ b/integration/compiler-mjs-cjs-output-test.ts @@ -0,0 +1,35 @@ +import { test, expect } from "@playwright/test"; +import * as fs from "node:fs"; +import * as path from "node:path"; + +import { createFixtureProject, js } from "./helpers/create-fixture.js"; + +test.describe("", () => { + for (let [serverModuleExt, serverModuleFormat, exportStatement] of [ + ["mjs", "esm", "export {"], + ["cjs", "cjs", "module.exports ="], + ]) { + test(`can write .${serverModuleExt} server output module`, async () => { + let projectDir = await createFixtureProject({ + files: { + // Ensure the config is valid ESM + "remix.config.js": js` + export default { + serverModuleFormat: "${serverModuleFormat}", + serverBuildPath: "build/index.${serverModuleExt}", + }; + `, + }, + }); + + let buildPath = path.resolve( + projectDir, + "build", + `index.${serverModuleExt}` + ); + expect(fs.existsSync(buildPath), "doesn't exist").toBe(true); + let contents = fs.readFileSync(buildPath, "utf8"); + expect(contents, "no export statement").toContain(exportStatement); + }); + } +}); diff --git a/integration/compiler-mjs-output-test.ts b/integration/compiler-mjs-output-test.ts deleted file mode 100644 index 65d112ad9ea..00000000000 --- a/integration/compiler-mjs-output-test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { test, expect } from "@playwright/test"; -import * as fs from "node:fs"; -import * as path from "node:path"; - -import { createFixtureProject, js, json } from "./helpers/create-fixture.js"; - -let projectDir: string; - -test.beforeAll(async () => { - projectDir = await createFixtureProject({ - files: { - // Ensure the config is valid ESM - "remix.config.js": js` - export default { - serverModuleFormat: "esm", - serverBuildPath: "build/index.mjs", - }; - `, - "package.json": json({ - name: "remix-template-remix", - private: true, - sideEffects: false, - type: "module", - dependencies: { - "@remix-run/node": "0.0.0-local-version", - "@remix-run/react": "0.0.0-local-version", - "@remix-run/serve": "0.0.0-local-version", - isbot: "0.0.0-local-version", - react: "0.0.0-local-version", - "react-dom": "0.0.0-local-version", - }, - devDependencies: { - "@remix-run/dev": "0.0.0-local-version", - "@types/react": "0.0.0-local-version", - "@types/react-dom": "0.0.0-local-version", - typescript: "0.0.0-local-version", - }, - engines: { - node: ">=18.0.0", - }, - }), - "app/routes/_index.tsx": js` - import { json } from "@remix-run/node"; - import { useLoaderData, Link } from "@remix-run/react"; - - export function loader() { - return json("pizza"); - } - - export default function Index() { - let data = useLoaderData(); - return ( - <div> - {data} - <Link to="/burgers">Other Route</Link> - </div> - ) - } - `, - }, - }); -}); - -test("can write .mjs server output module", () => { - let buildPath = path.resolve(projectDir, "build", "index.mjs"); - expect(fs.existsSync(buildPath), "doesn't exist").toBe(true); - let contents = fs.readFileSync(buildPath, "utf8"); - expect(contents, "no export statement").toContain("export {"); -}); diff --git a/packages/remix-dev/compiler/server/write.ts b/packages/remix-dev/compiler/server/write.ts index 948d1a0b5a4..d5300452d1b 100644 --- a/packages/remix-dev/compiler/server/write.ts +++ b/packages/remix-dev/compiler/server/write.ts @@ -11,7 +11,7 @@ export async function write( await fse.ensureDir(path.dirname(config.serverBuildPath)); for (let file of outputFiles) { - if (file.path.endsWith(".js") || file.path.endsWith(".mjs")) { + if ([".js", ".cjs", ".mjs"].some((ext) => file.path.endsWith(ext))) { // fix sourceMappingURL to be relative to current path instead of /build let filename = file.path.substring(file.path.lastIndexOf(path.sep) + 1); let escapedFilename = filename.replace(/\./g, "\\."); From 87234cbd7dbbb577d926b416b8e568ef9643bf36 Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Fri, 8 Sep 2023 15:28:57 -0400 Subject: [PATCH 37/46] Revert back to latest versions of node in CI (#7385) --- .github/workflows/test-full.yml | 10 +++++----- .github/workflows/test-pr-ubuntu.yml | 4 ++-- .github/workflows/test-pr-windows-macos.yml | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-full.yml b/.github/workflows/test-full.yml index cc6bbf21586..9eb2c9f9bad 100644 --- a/.github/workflows/test-full.yml +++ b/.github/workflows/test-full.yml @@ -29,7 +29,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "ubuntu-latest" - node_version: "['18', '20.5']" + node_version: "[18, 20]" unit-windows: name: "🧪 Unit Test" @@ -37,7 +37,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "windows-latest" - node_version: "['18', '20.5']" + node_version: "[18, 20]" integration-ubuntu: name: "👀 Integration Test" @@ -45,7 +45,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "ubuntu-latest" - node_version: "['18', '20.5']" + node_version: "[18, 20]" browser: '["chromium", "firefox"]' integration-windows: @@ -54,7 +54,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "windows-latest" - node_version: "['18', '20.5']" + node_version: "[18, 20]" browser: '["msedge"]' integration-macos: @@ -63,5 +63,5 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "macos-latest" - node_version: "['18', '20.5']" + node_version: "[18, 20]" browser: '["webkit"]' diff --git a/.github/workflows/test-pr-ubuntu.yml b/.github/workflows/test-pr-ubuntu.yml index 4cecb5d0ab9..2e7b86eef3e 100644 --- a/.github/workflows/test-pr-ubuntu.yml +++ b/.github/workflows/test-pr-ubuntu.yml @@ -26,7 +26,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "ubuntu-latest" - node_version: '["20.5.1"]' + node_version: '["latest"]' integration-chromium: name: "👀 Integration Test" @@ -34,5 +34,5 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "ubuntu-latest" - node_version: '["20.5.1"]' + node_version: '["latest"]' browser: '["chromium"]' diff --git a/.github/workflows/test-pr-windows-macos.yml b/.github/workflows/test-pr-windows-macos.yml index 6115528bf98..d0affe8f70e 100644 --- a/.github/workflows/test-pr-windows-macos.yml +++ b/.github/workflows/test-pr-windows-macos.yml @@ -20,7 +20,7 @@ jobs: uses: ./.github/workflows/shared-test-unit.yml with: os: "windows-latest" - node_version: '["20.5.1"]' + node_version: '["latest"]' integration-firefox: name: "👀 Integration Test" @@ -28,7 +28,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "ubuntu-latest" - node_version: '["20.5.1"]' + node_version: '["latest"]' browser: '["firefox"]' integration-msedge: @@ -37,7 +37,7 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "windows-latest" - node_version: '["20.5.1"]' + node_version: '["latest"]' browser: '["msedge"]' integration-webkit: @@ -46,5 +46,5 @@ jobs: uses: ./.github/workflows/shared-test-integration.yml with: os: "macos-latest" - node_version: '["20.5.1"]' + node_version: '["latest"]' browser: '["webkit"]' From c3890e6a047379380321eeeaad6df206718a2529 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Wed, 13 Sep 2023 11:27:57 -0600 Subject: [PATCH 38/46] docs: moving stuff around --- docs/discussion/09-middleware.md | 6 ------ .../{18-component-data.md => component-data.md} | 0 .../{09-concurrency.md => concurrency.md} | 0 ...s-and-sessions.md => cookies-and-sessions.md} | 0 .../discussion/{03-data-flow.md => data-flow.md} | 1 + .../{14-error-handling.md => error-handling.md} | 0 ...{12-form-validation.md => form-validation.md} | 0 ...{10-form-vs-fetcher.md => form-vs-fetcher.md} | 0 docs/discussion/{11-formdata.md => formdata.md} | 0 ...tcher.md => forms-vs-usesubmit-vs-fetcher.md} | 0 .../{17-html-forms.md => html-forms.md} | 0 .../{00-introduction.md => introduction.md} | 2 +- .../{15-multiple-forms.md => multiple-forms.md} | 0 .../{10-nested-routes.md => nested-routes.md} | 0 .../{07-pending-ui.md => pending-ui.md} | 1 + ...enhancement.md => progressive-enhancement.md} | 1 + .../{05-react-router.md => react-router.md} | 1 + docs/discussion/{02-routes.md => routes.md} | 1 + docs/discussion/{01-runtimes.md => runtimes.md} | 1 + ...4-server-vs-client.md => server-vs-client.md} | 1 + ...8-state-management.md => state-management.md} | 1 + docs/discussion/{19-testing.md => testing.md} | 0 docs/guides/14-error-handling.md | 16 ---------------- docs/start/community.md | 2 +- docs/start/future-flags.md | 2 +- docs/{guides => start}/v2.md | 3 +-- 26 files changed, 12 insertions(+), 27 deletions(-) delete mode 100644 docs/discussion/09-middleware.md rename docs/discussion/{18-component-data.md => component-data.md} (100%) rename docs/discussion/{09-concurrency.md => concurrency.md} (100%) rename docs/discussion/{13-cookies-and-sessions.md => cookies-and-sessions.md} (100%) rename docs/discussion/{03-data-flow.md => data-flow.md} (99%) rename docs/discussion/{14-error-handling.md => error-handling.md} (100%) rename docs/discussion/{12-form-validation.md => form-validation.md} (100%) rename docs/discussion/{10-form-vs-fetcher.md => form-vs-fetcher.md} (100%) rename docs/discussion/{11-formdata.md => formdata.md} (100%) rename docs/discussion/{16-forms-vs-usesubmit-vs-fetcher.md => forms-vs-usesubmit-vs-fetcher.md} (100%) rename docs/discussion/{17-html-forms.md => html-forms.md} (100%) rename docs/discussion/{00-introduction.md => introduction.md} (99%) rename docs/discussion/{15-multiple-forms.md => multiple-forms.md} (100%) rename docs/discussion/{10-nested-routes.md => nested-routes.md} (100%) rename docs/discussion/{07-pending-ui.md => pending-ui.md} (99%) rename docs/discussion/{06-progressive-enhancement.md => progressive-enhancement.md} (99%) rename docs/discussion/{05-react-router.md => react-router.md} (99%) rename docs/discussion/{02-routes.md => routes.md} (99%) rename docs/discussion/{01-runtimes.md => runtimes.md} (99%) rename docs/discussion/{04-server-vs-client.md => server-vs-client.md} (99%) rename docs/discussion/{08-state-management.md => state-management.md} (99%) rename docs/discussion/{19-testing.md => testing.md} (100%) delete mode 100644 docs/guides/14-error-handling.md rename docs/{guides => start}/v2.md (99%) diff --git a/docs/discussion/09-middleware.md b/docs/discussion/09-middleware.md deleted file mode 100644 index 81650bd2d78..00000000000 --- a/docs/discussion/09-middleware.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Middleware -hidden: true ---- - -# Middleware diff --git a/docs/discussion/18-component-data.md b/docs/discussion/component-data.md similarity index 100% rename from docs/discussion/18-component-data.md rename to docs/discussion/component-data.md diff --git a/docs/discussion/09-concurrency.md b/docs/discussion/concurrency.md similarity index 100% rename from docs/discussion/09-concurrency.md rename to docs/discussion/concurrency.md diff --git a/docs/discussion/13-cookies-and-sessions.md b/docs/discussion/cookies-and-sessions.md similarity index 100% rename from docs/discussion/13-cookies-and-sessions.md rename to docs/discussion/cookies-and-sessions.md diff --git a/docs/discussion/03-data-flow.md b/docs/discussion/data-flow.md similarity index 99% rename from docs/discussion/03-data-flow.md rename to docs/discussion/data-flow.md index 22de4f32753..64ce2a5bb28 100644 --- a/docs/discussion/03-data-flow.md +++ b/docs/discussion/data-flow.md @@ -1,5 +1,6 @@ --- title: Fullstack Data Flow +order: 4 --- # Fullstack Data Flow diff --git a/docs/discussion/14-error-handling.md b/docs/discussion/error-handling.md similarity index 100% rename from docs/discussion/14-error-handling.md rename to docs/discussion/error-handling.md diff --git a/docs/discussion/12-form-validation.md b/docs/discussion/form-validation.md similarity index 100% rename from docs/discussion/12-form-validation.md rename to docs/discussion/form-validation.md diff --git a/docs/discussion/10-form-vs-fetcher.md b/docs/discussion/form-vs-fetcher.md similarity index 100% rename from docs/discussion/10-form-vs-fetcher.md rename to docs/discussion/form-vs-fetcher.md diff --git a/docs/discussion/11-formdata.md b/docs/discussion/formdata.md similarity index 100% rename from docs/discussion/11-formdata.md rename to docs/discussion/formdata.md diff --git a/docs/discussion/16-forms-vs-usesubmit-vs-fetcher.md b/docs/discussion/forms-vs-usesubmit-vs-fetcher.md similarity index 100% rename from docs/discussion/16-forms-vs-usesubmit-vs-fetcher.md rename to docs/discussion/forms-vs-usesubmit-vs-fetcher.md diff --git a/docs/discussion/17-html-forms.md b/docs/discussion/html-forms.md similarity index 100% rename from docs/discussion/17-html-forms.md rename to docs/discussion/html-forms.md diff --git a/docs/discussion/00-introduction.md b/docs/discussion/introduction.md similarity index 99% rename from docs/discussion/00-introduction.md rename to docs/discussion/introduction.md index 4aa60b3a0f3..77721c50c6c 100644 --- a/docs/discussion/00-introduction.md +++ b/docs/discussion/introduction.md @@ -1,6 +1,6 @@ --- title: Introduction, Technical Explanation -order: 0 +order: 1 --- # Introduction, Technical Explanation diff --git a/docs/discussion/15-multiple-forms.md b/docs/discussion/multiple-forms.md similarity index 100% rename from docs/discussion/15-multiple-forms.md rename to docs/discussion/multiple-forms.md diff --git a/docs/discussion/10-nested-routes.md b/docs/discussion/nested-routes.md similarity index 100% rename from docs/discussion/10-nested-routes.md rename to docs/discussion/nested-routes.md diff --git a/docs/discussion/07-pending-ui.md b/docs/discussion/pending-ui.md similarity index 99% rename from docs/discussion/07-pending-ui.md rename to docs/discussion/pending-ui.md index 8cc213eaffc..769bac2848c 100644 --- a/docs/discussion/07-pending-ui.md +++ b/docs/discussion/pending-ui.md @@ -1,5 +1,6 @@ --- title: Pending UI +order: 8 --- # Pending and Optimistic UI diff --git a/docs/discussion/06-progressive-enhancement.md b/docs/discussion/progressive-enhancement.md similarity index 99% rename from docs/discussion/06-progressive-enhancement.md rename to docs/discussion/progressive-enhancement.md index c2441ec1c9b..c89284afbe2 100644 --- a/docs/discussion/06-progressive-enhancement.md +++ b/docs/discussion/progressive-enhancement.md @@ -1,5 +1,6 @@ --- title: Progressive Enhancement +order: 7 --- # Progressive Enhancement diff --git a/docs/discussion/05-react-router.md b/docs/discussion/react-router.md similarity index 99% rename from docs/discussion/05-react-router.md rename to docs/discussion/react-router.md index c134243de48..e1b3caac58c 100644 --- a/docs/discussion/05-react-router.md +++ b/docs/discussion/react-router.md @@ -1,5 +1,6 @@ --- title: React Router +order: 6 --- # React Router diff --git a/docs/discussion/02-routes.md b/docs/discussion/routes.md similarity index 99% rename from docs/discussion/02-routes.md rename to docs/discussion/routes.md index 9556a16d974..e7ac8385d1a 100644 --- a/docs/discussion/02-routes.md +++ b/docs/discussion/routes.md @@ -1,5 +1,6 @@ --- title: Route Configuration +order: 3 --- # Route Configuration diff --git a/docs/discussion/01-runtimes.md b/docs/discussion/runtimes.md similarity index 99% rename from docs/discussion/01-runtimes.md rename to docs/discussion/runtimes.md index 288ba4fe3bb..97ed6fed1c7 100644 --- a/docs/discussion/01-runtimes.md +++ b/docs/discussion/runtimes.md @@ -1,5 +1,6 @@ --- title: Runtimes, Adapters, Templates, and Deployment +order: 2 --- # Runtimes, Adapters, Templates, and Deployment diff --git a/docs/discussion/04-server-vs-client.md b/docs/discussion/server-vs-client.md similarity index 99% rename from docs/discussion/04-server-vs-client.md rename to docs/discussion/server-vs-client.md index f007a86876d..690a73c37c4 100644 --- a/docs/discussion/04-server-vs-client.md +++ b/docs/discussion/server-vs-client.md @@ -1,5 +1,6 @@ --- title: Server vs. Client Code Execution +order: 5 --- # Server vs. Client Code Execution diff --git a/docs/discussion/08-state-management.md b/docs/discussion/state-management.md similarity index 99% rename from docs/discussion/08-state-management.md rename to docs/discussion/state-management.md index 0de26c36f57..9668516e37d 100644 --- a/docs/discussion/08-state-management.md +++ b/docs/discussion/state-management.md @@ -1,5 +1,6 @@ --- title: State Management +order: 9 --- # State Management diff --git a/docs/discussion/19-testing.md b/docs/discussion/testing.md similarity index 100% rename from docs/discussion/19-testing.md rename to docs/discussion/testing.md diff --git a/docs/guides/14-error-handling.md b/docs/guides/14-error-handling.md deleted file mode 100644 index d4043d12ef3..00000000000 --- a/docs/guides/14-error-handling.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Error Handling -hidden: true ---- - -# Error Handling - -- unexpected - - automatically handled - - granular w/ route boundaries - - granular w/ `<Await>` boundaries -- expected - - 404s - - 401s - - 503s - - can send data! diff --git a/docs/start/community.md b/docs/start/community.md index 2f58b4a89e8..6e588845b2e 100644 --- a/docs/start/community.md +++ b/docs/start/community.md @@ -1,7 +1,7 @@ --- title: Community description: Community resources for learning Remix and related technologies -order: 3 +order: 4 --- # Community diff --git a/docs/start/future-flags.md b/docs/start/future-flags.md index 711d41993d2..b468363ca65 100644 --- a/docs/start/future-flags.md +++ b/docs/start/future-flags.md @@ -1,6 +1,6 @@ --- title: Future Flags -order: 4 +order: 5 --- # Gradual Feature Adoption with Future Flags diff --git a/docs/guides/v2.md b/docs/start/v2.md similarity index 99% rename from docs/guides/v2.md rename to docs/start/v2.md index 75b6cc5bb46..838a6a88b21 100644 --- a/docs/guides/v2.md +++ b/docs/start/v2.md @@ -1,7 +1,6 @@ --- title: Upgrading to v2 -order: 1 -new: true +order: 3 --- # Upgrading to v2 From 6329591ba22fce06586e0e0f5357d103569c0bfb Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Wed, 13 Sep 2023 11:47:52 -0600 Subject: [PATCH 39/46] docs: fix links to moved files --- docs/components/form.md | 8 ++++---- docs/discussion/concurrency.md | 2 +- docs/discussion/form-vs-fetcher.md | 2 +- docs/discussion/forms-vs-usesubmit-vs-fetcher.md | 6 ------ docs/discussion/progressive-enhancement.md | 2 +- docs/discussion/state-management.md | 4 ++-- docs/hooks/use-action-data.md | 2 +- docs/hooks/use-fetcher.md | 4 ++-- docs/hooks/use-fetchers.md | 4 ++-- docs/hooks/use-loader-data.md | 4 ++-- docs/start/quickstart.md | 2 +- 11 files changed, 17 insertions(+), 23 deletions(-) delete mode 100644 docs/discussion/forms-vs-usesubmit-vs-fetcher.md diff --git a/docs/components/form.md b/docs/components/form.md index e1ad63f289d..b1e7d887331 100644 --- a/docs/components/form.md +++ b/docs/components/form.md @@ -113,8 +113,8 @@ See also: [data-mutations-with-form-action]: https://www.youtube.com/watch?v=Iv25HAHaFDs&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 [multiple-forms-and-single-button-mutations]: https://www.youtube.com/watch?v=w2i-9cYxSdc&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 [clearing-inputs-after-form-submissions]: https://www.youtube.com/watch?v=bMLej7bg5Zo&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 -[fullstack-data-flow]: ../discussion/03-data-flow -[pending-ui]: ../discussion/07-pending-ui -[form-vs-fetcher]: ../discussion/10-form-vs-fetcher +[fullstack-data-flow]: ../discussion/data-flow +[pending-ui]: ../discussion/pending-ui +[form-vs-fetcher]: ../discussion/form-vs-fetcher [fetcher-form]: ../hooks/use-fetcher -[progressive-enhancement]: ../discussion/06-progressive-enhancement +[progressive-enhancement]: ../discussion/progressive-enhancement diff --git a/docs/discussion/concurrency.md b/docs/discussion/concurrency.md index 79dcb8d3583..c3d9954307b 100644 --- a/docs/discussion/concurrency.md +++ b/docs/discussion/concurrency.md @@ -123,4 +123,4 @@ All the application needs to know is how to query the data and how to render it, Remix offers developers an intuitive, browser-based approach to managing network requests. By mirroring browser behaviors and enhancing them where needed, it simplifies the complexities of concurrency, revalidation, and potential race conditions. Whether you're building a simple webpage or a sophisticated web application, Remix ensures that your user interactions are smooth, reliable, and always up-to-date. -[fullstack-data-flow]: ./03-data-flow +[fullstack-data-flow]: ./data-flow diff --git a/docs/discussion/form-vs-fetcher.md b/docs/discussion/form-vs-fetcher.md index 2ad7fc0eceb..d33a74e400b 100644 --- a/docs/discussion/form-vs-fetcher.md +++ b/docs/discussion/form-vs-fetcher.md @@ -256,4 +256,4 @@ function UserAvatar({ partialUser }) { Remix offers a range of tools to cater to varied developmental needs. While some functionalities might seem to overlap, each tool has been crafted with specific scenarios in mind. By understanding the intricacies and ideal applications of `<Form>`, `useSubmit`, `useNavigation`, `useActionData`, and `useFetcher`, developers can create more intuitive, responsive, and user-friendly web applications. -[network-concurrency-management]: ./09-concurrency +[network-concurrency-management]: ./concurrency diff --git a/docs/discussion/forms-vs-usesubmit-vs-fetcher.md b/docs/discussion/forms-vs-usesubmit-vs-fetcher.md deleted file mode 100644 index 8853712ed96..00000000000 --- a/docs/discussion/forms-vs-usesubmit-vs-fetcher.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Form vs. useSubmit vs. fetcher -hidden: true ---- - -# Form vs. useSubmit vs. fetcher diff --git a/docs/discussion/progressive-enhancement.md b/docs/discussion/progressive-enhancement.md index c89284afbe2..7cf8d51d207 100644 --- a/docs/discussion/progressive-enhancement.md +++ b/docs/discussion/progressive-enhancement.md @@ -140,4 +140,4 @@ No fundamental change in architecture, simply a progressive enhancement for both See also: [State Management][state-management] [wikipedia]: https://en.wikipedia.org/wiki/Progressive_enhancement -[state-management]: ./08-state-management +[state-management]: ./state-management diff --git a/docs/discussion/state-management.md b/docs/discussion/state-management.md index 9668516e37d..f523a8824e2 100644 --- a/docs/discussion/state-management.md +++ b/docs/discussion/state-management.md @@ -501,5 +501,5 @@ As bonus party trick, the form is functional even before JavaScript loads. Inste If you ever find yourself entangled in managing and synchronizing state for network operations, Remix likely offers a more elegant solution. -[fullstack-data-flow]: ./03-data-flow -[pending-ui]: ./07-pending-ui +[fullstack-data-flow]: ./data-flow +[pending-ui]: ./pending-ui diff --git a/docs/hooks/use-action-data.md b/docs/hooks/use-action-data.md index a1fc6fc5414..5bb886b324f 100644 --- a/docs/hooks/use-action-data.md +++ b/docs/hooks/use-action-data.md @@ -47,4 +47,4 @@ export default function Invoices() { [usenavigation]: ../hooks/use-navigation [rr-useactiondata]: https://reactrouter.com/hooks/use-action-data [form-validation]: ../guides/form-validation -[fullstack-data-flow]: ../discussion/03-data-flow +[fullstack-data-flow]: ../discussion/data-flow diff --git a/docs/hooks/use-fetcher.md b/docs/hooks/use-fetcher.md index 6e4d1a9da3b..569c3007c88 100644 --- a/docs/hooks/use-fetcher.md +++ b/docs/hooks/use-fetcher.md @@ -106,7 +106,7 @@ The form method of the submission. - [Concurrent Mutations w/ useFetcher][concurrent-mutations-w-use-fetcher] - [Optimistic UI][optimistic-ui] -[form-vs-fetcher]: ../discussion/10-form-vs-fetcher -[network-concurrency-management]: ../discussion/09-concurrency +[form-vs-fetcher]: ../discussion/form-vs-fetcher +[network-concurrency-management]: ../discussion/concurrency [concurrent-mutations-w-use-fetcher]: https://www.youtube.com/watch?v=vTzNpiOk668&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 [optimistic-ui]: https://www.youtube.com/watch?v=EdB_nj01C80&list=PLXoynULbYuEDG2wBFSZ66b85EIspy3fy6 diff --git a/docs/hooks/use-fetchers.md b/docs/hooks/use-fetchers.md index bae977c2c4d..538b2565eb9 100644 --- a/docs/hooks/use-fetchers.md +++ b/docs/hooks/use-fetchers.md @@ -31,6 +31,6 @@ The fetchers don't contain `fetcher.Form`, `fetcher.submit`, or `fetcher.load`, - [`useFetcher`][use-fetcher] -[form-vs-fetcher]: ../discussion/10-form-vs-fetcher -[pending-optimistic-ui]: ../discussion/07-pending-ui +[form-vs-fetcher]: ../discussion/form-vs-fetcher +[pending-optimistic-ui]: ../discussion/pending-ui [use-fetcher]: ./use-fetcher diff --git a/docs/hooks/use-loader-data.md b/docs/hooks/use-loader-data.md index cb21ac95fee..0aebbcda9e8 100644 --- a/docs/hooks/use-loader-data.md +++ b/docs/hooks/use-loader-data.md @@ -32,7 +32,7 @@ export default function Invoices() { - [`loader`][loader] - [`useFetcher`][use-fetcher] -[fullstack-data-flow]: ../discussion/03-data-flow -[state-management]: ../discussion/08-state-management +[fullstack-data-flow]: ../discussion/data-flow +[state-management]: ../discussion/state-management [loader]: ../route/loader [use-fetcher]: ./use-fetcher diff --git a/docs/start/quickstart.md b/docs/start/quickstart.md index 5da6dac5f16..77ff7c32459 100644 --- a/docs/start/quickstart.md +++ b/docs/start/quickstart.md @@ -257,7 +257,7 @@ What's next? - [Tutorial][tutorial] -[runtimes]: ../discussion/01-runtimes +[runtimes]: ../discussion/runtimes [inspect]: https://nodejs.org/en/docs/guides/debugging-getting-started/ [tutorial]: ./tutorial [remix-config]: ../file-conventions/remix-config From 4d732717c9758ae1ef87545e9adc88b0e25e5672 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Wed, 13 Sep 2023 11:49:38 -0600 Subject: [PATCH 40/46] docs: nothin --- docs/discussion/introduction.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/discussion/introduction.md b/docs/discussion/introduction.md index 77721c50c6c..c01bc47be7e 100644 --- a/docs/discussion/introduction.md +++ b/docs/discussion/introduction.md @@ -5,8 +5,6 @@ order: 1 # Introduction, Technical Explanation -These discussion topics are intended to be read in order. It gives you a linear path to Remix mastery instead of bouncing around from one doc to another. While useful independently, topics may refer to code and concepts from previous chapters. - Built on top of [React Router][reactrouter], Remix is four things: 1. A compiler From 3646f9176799dbe70d4e3731908faaeba5b77c17 Mon Sep 17 00:00:00 2001 From: Ryan Florence <rpflorence@gmail.com> Date: Wed, 13 Sep 2023 12:05:06 -0600 Subject: [PATCH 41/46] docs: update main index page Need to update the links to main/ instead of dev/ in the release branch --- docs/index.md | 59 +++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/docs/index.md b/docs/index.md index bc2471272d3..0e529168504 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,57 +12,42 @@ hidden: true npx create-remix@latest ``` -## Getting Started - <docs-cards> - <a href="/pages/technical-explanation" aria-label="Technical Explanation"> + <a href="dev/discussion/introduction" aria-label="Technical Explanation"> <docs-card> - <h4>Technical Explanation</h4> - <p>If you're wondering what Remix is, this is the page for you. Learn how Remix is four primary things: a compiler, an HTTP handler, a server framework, and a browser framework.</p> + <h4 class="text-blue-brand">I'm just curious...</h4> + <p>Start with the <span style="text-decoration:underline">Technical Explanation</span>. Remix is a new kind of web framework that we like to call "centerstack". It blends old and new web development models in a unique way that deserves some explanation!</p> </docs-card> </a> - <a href="/pages/v2" aria-label="Upgrading to v2"> + <a href="dev/start/tutorial" aria-label="Developer Blog Tutorial"> <docs-card> - <h4>Upgrading to v2</h4> - <p>Remix v2 is here! All of the new APIs and behaviors are available already in v1 behind Future Flags. This doc will help you incrementally adopt each one so that when it's time to update to v2, you don't have to change a thing.</p> + <h4 class="text-green-brand">I want to try it out...</h4> + <p>Spend your first few minutes with Remix in the <span style="text-decoration:underline">Tutorial</span>. It introduces the core features as quickly as possible building a little contact management app. You'll see data loading, actions, form validation, search, redirects, and more.</p> </docs-card> </a> - <a href="/tutorials/blog" aria-label="Developer Blog Tutorial"> + <a href="dev/discussion/runtimes" aria-label="Runtimes, Adapters, Stacks, and Deployment"> <docs-card> - <h4>Blog Tutorial</h4> - <p>Spend your first few minutes with Remix here and let us introduce some of the core features as quickly as possible. After this, you can go explore the docs or dive deeper with the other tutorials. We'll build a little markdown blog with data loading, actions, form validation, redirects, and more.</p> + <h4 class="text-pink-brand">I'm in, let's go.</h4> + <p>The <b>Discussions</b> will help you get a deep understanding of Remix. Get yourself a drink and some snacks then dive deep into building better web apps with Remix.</p> </docs-card> </a> - <a href="/tutorials/jokes" aria-label="Jokes App Tutorial"> + <a href="dev/start/community" aria-label="Remix API"> <docs-card> - <h4>Jokes App Deep Dive</h4> - <p>Dive deep into Remix and full stack development with this app. It's backed by a SQL database, user authentication and session, and of course some modern UI finishes. You'll learn about nested routes, sessions, data loading, data mutations, progressive enhancement, and more. Get a feel for what a data-backed web app feels like with Remix.</p> + <h4 class="text-red-brand">I'm stuck!</h4> + <p>Need help? Want to contribute? Remix is developed in the open with an helpful community. Come and see where to find us on the <b>Community</b> page, follow along with Remix development, get help, and contribute back!</p> </docs-card> </a> </docs-cards> -## Community +<!-- -<docs-cards> - <a href="https://rmx.as/github" aria-label="GitHub Repository"> - <docs-card> - <h4>GitHub Repository</h4> - <p>Follow along and contribute to the development of Remix. We actually love typo PRs.</p> - <p>GitHub Discussions is also the best place to get help when you're stuck. Other folks can post answers for the next person to see.</p> - </docs-card> - </a> - <a href="https://github.com/remix-run/examples" aria-label="Remix Examples"> - <docs-card> - <h4>Remix Examples</h4> - <p>Have a look at the various Remix examples made by the community.</p> - </docs-card> - </a> - <a href="https://rmx.as/discord" aria-label="Remix Discord"> - <docs-card> - <h4>Discord Server</h4> - <p>Join our awesome community of developers for realtime discussion, help, and showing off what you built! Good vibes only.</p> - </docs-card> - </a> -</docs-cards> +{Add this when I'm done moving things around} + +## How to Use These Docs + +- **Tutorials**: These are step-by-step guides that walk you through building a specific app. They're great for getting started with Remix and learning the basics. +- **Discussions**: These help you understand Remix by diving into a topic and how various APIs work together to meet use cases or explain some behavior that might not be obvious just from the API. +- **Reference**: These are the docs for the APIs and conventions that Remix provides. They're great for looking up how to use a specific API or feature but don't contain a lot of conversation about how to use them together. +- **Guides**: They're great for learning how to use Remix in a specific way or for a specific use case. -[git-hub]: https://github.com/remix-run/remix +--> From c659c687943b3470ad42e53497afcc0d0438b95e Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Fri, 15 Sep 2023 08:55:00 -0400 Subject: [PATCH 42/46] docs(file-conventions/entry.server): add note about aborted requests in `handleError` section (#7418) --- docs/file-conventions/entry.server.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/file-conventions/entry.server.md b/docs/file-conventions/entry.server.md index 9b9262d87bc..adfc7a81a3a 100644 --- a/docs/file-conventions/entry.server.md +++ b/docs/file-conventions/entry.server.md @@ -32,11 +32,15 @@ export function handleError( error: unknown, { request, params, context }: DataFunctionArgs ) { - sendErrorToErrorReportingService(error); - console.error(formatErrorForJsonLogging(error)); + if (!request.signal.aborted) { + sendErrorToErrorReportingService(error); + console.error(formatErrorForJsonLogging(error)); + } } ``` +_Note that you generally want to avoid logging when the request was aborted, since Remix's cancellation and race-condition handling can cause a lot of requests to be aborted._ + ### Streaming Rendering Errors When you are streaming your HTML responses via [`renderToPipeableStream`][rendertopipeablestream] or [`renderToReadableStream`][rendertoreadablestream], your own `handleError` implementation will only handle errors encountered during the initial shell render. If you encounter a rendering error during subsequent streamed rendering you will need handle these errors manually since the Remix server has already sent the Response by that point. From e3d5b17280658c9be1a38ce1b54757345884b14a Mon Sep 17 00:00:00 2001 From: Remix Run Bot <hello@remix.run> Date: Fri, 15 Sep 2023 18:16:20 +0000 Subject: [PATCH 43/46] chore: format --- docs/start/tutorial.md | 2 +- packages/remix-deno/CHANGELOG.md | 29 +++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/start/tutorial.md b/docs/start/tutorial.md index 71e68ab481e..8476f6caa25 100644 --- a/docs/start/tutorial.md +++ b/docs/start/tutorial.md @@ -36,7 +36,7 @@ npm install npm run dev ``` -You should be able to open up [http://localhost:3000][http-localhost-3000] and see an unstyled screen that looks like this: +You should be able to open up \[http\://localhost:3000]\[http-localhost-3000] and see an unstyled screen that looks like this: <img class="tutorial" src="/docs-images/contacts/03.webp" /> diff --git a/packages/remix-deno/CHANGELOG.md b/packages/remix-deno/CHANGELOG.md index 25ddc96c0d8..e3fe8cb82c9 100644 --- a/packages/remix-deno/CHANGELOG.md +++ b/packages/remix-deno/CHANGELOG.md @@ -4,27 +4,40 @@ ### Major Changes -- Removed/adjusted types to prefer `unknown` over `any` and to align with underlying React Router types ([#7319](https://github.com/remix-run/remix/pull/7319), [#7354](https://github.com/remix-run/remix/pull/7354)): +- Removed/adjusted types to prefer `unknown` over `any` and to align with + underlying React Router types + ([#7319](https://github.com/remix-run/remix/pull/7319), + [#7354](https://github.com/remix-run/remix/pull/7354)): - Renamed the `useMatches()` return type from `RouteMatch` to `UIMatch` - - Renamed `LoaderArgs`/`ActionArgs` to `LoaderFunctionArgs`/`ActionFunctionArgs` + - Renamed `LoaderArgs`/`ActionArgs` to + `LoaderFunctionArgs`/`ActionFunctionArgs` - `AppData` changed from `any` to `unknown` - `Location["state"]` (`useLocation.state`) changed from `any` to `unknown` - `UIMatch["data"]` (`useMatches()[i].data`) changed from `any` to `unknown` - - `UIMatch["handle"]` (`useMatches()[i].handle`) changed from `{ [k: string]: any }` to `unknown` + - `UIMatch["handle"]` (`useMatches()[i].handle`) changed from + `{ [k: string]: any }` to `unknown` - `Fetcher["data"]` (`useFetcher().data`) changed from `any` to `unknown` - `MetaMatch.handle` (used in `meta()`) changed from `any` to `unknown` - - `AppData`/`RouteHandle` are no longer exported as they are just aliases for `unknown` + - `AppData`/`RouteHandle` are no longer exported as they are just aliases for + `unknown` - Require Node >=18.0.0 ([#6939](https://github.com/remix-run/remix/pull/6939)) -- The route `meta` API now defaults to the new "V2 Meta" API ([#6958](https://github.com/remix-run/remix/pull/6958)) - - Please refer to the ([docs](https://remix.run/docs/en/2.0.0/route/meta) and [Preparing for V2](https://remix.run/docs/en/2.0.0/start/v2#route-meta) guide for more information. +- The route `meta` API now defaults to the new "V2 Meta" API + ([#6958](https://github.com/remix-run/remix/pull/6958)) + - Please refer to the ([docs](https://remix.run/docs/en/2.0.0/route/meta) and + [Preparing for V2](https://remix.run/docs/en/2.0.0/start/v2#route-meta) + guide for more information. ### Minor Changes -- Re-export the new `redirectDocument` method from React Router ([#7040](https://github.com/remix-run/remix/pull/7040), [#6842](https://github.com/remix-run/remix/pull/6842)) ([#7040](https://github.com/remix-run/remix/pull/7040)) +- Re-export the new `redirectDocument` method from React Router + ([#7040](https://github.com/remix-run/remix/pull/7040), + [#6842](https://github.com/remix-run/remix/pull/6842)) + ([#7040](https://github.com/remix-run/remix/pull/7040)) ### Patch Changes -- Export proper `ErrorResponse` type for usage alongside `isRouteErrorResponse` ([#7244](https://github.com/remix-run/remix/pull/7244)) +- Export proper `ErrorResponse` type for usage alongside `isRouteErrorResponse` + ([#7244](https://github.com/remix-run/remix/pull/7244)) - Updated dependencies: - `@remix-run/server-runtime@2.0.0` From 19d66ed8a69399fcc0dc3928c3875ccbc9e0b687 Mon Sep 17 00:00:00 2001 From: Pedro Cattori <pcattori@gmail.com> Date: Mon, 18 Sep 2023 12:39:14 -0400 Subject: [PATCH 44/46] in manual mode, watch for changes to version.txt instead of server build (#7470) --- .changeset/four-spies-draw.md | 7 +++++++ packages/remix-serve/cli.ts | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .changeset/four-spies-draw.md diff --git a/.changeset/four-spies-draw.md b/.changeset/four-spies-draw.md new file mode 100644 index 00000000000..a97459b20eb --- /dev/null +++ b/.changeset/four-spies-draw.md @@ -0,0 +1,7 @@ +--- +"@remix-run/serve": patch +--- + +Fix error caused by partially written server build + +Previously, it was possible to trigger a reimport of the app server code before the new server build had completely been written. Reimporting the partially written server build caused issues related to `build.assets` being undefined and crashing when reading `build.assets.version`. diff --git a/packages/remix-serve/cli.ts b/packages/remix-serve/cli.ts index 645c6867d75..670fb8e08fa 100644 --- a/packages/remix-serve/cli.ts +++ b/packages/remix-serve/cli.ts @@ -42,6 +42,7 @@ async function run() { } let buildPath = path.resolve(buildPathArg); + let versionPath = path.resolve(buildPath, "..", "version.txt"); async function reimportServer() { let stat = fs.statSync(buildPath); @@ -60,7 +61,7 @@ async function run() { } chokidar - .watch(buildPath, { ignoreInitial: true }) + .watch(versionPath, { ignoreInitial: true }) .on("add", handleServerUpdate) .on("change", handleServerUpdate); From 566d3abd57b081032cc62b4a3c5759a6c8fa5aba Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Mon, 18 Sep 2023 14:56:30 -0400 Subject: [PATCH 45/46] Add second generic to UIMatch for handle field (#7464) --- .changeset/add-uimatch-handle.md | 5 +++++ packages/remix-react/components.tsx | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .changeset/add-uimatch-handle.md diff --git a/.changeset/add-uimatch-handle.md b/.changeset/add-uimatch-handle.md new file mode 100644 index 00000000000..20cafbdfcd4 --- /dev/null +++ b/.changeset/add-uimatch-handle.md @@ -0,0 +1,5 @@ +--- +"@remix-run/react": patch +--- + +Add second generic to `UIMatch` for `handle` field diff --git a/packages/remix-react/components.tsx b/packages/remix-react/components.tsx index 72b2a2ab610..de97f2f88fa 100644 --- a/packages/remix-react/components.tsx +++ b/packages/remix-react/components.tsx @@ -52,6 +52,7 @@ import type { MetaDescriptor, MetaMatch, MetaMatches, + RouteHandle, } from "./routeModules"; function useDataRouterContext() { @@ -976,7 +977,10 @@ function dedupe(array: any[]) { return [...new Set(array)]; } -export type UIMatch<D = AppData> = UIMatchRR<SerializeFrom<D>>; +export type UIMatch<D = AppData, H = RouteHandle> = UIMatchRR< + SerializeFrom<D>, + H +>; /** * Returns the active route matches, useful for accessing loaderData for From fccadba0efc9e8d32a93cb8c0a9b0cc6e07e2ab0 Mon Sep 17 00:00:00 2001 From: Matt Brophy <matt@brophy.org> Date: Mon, 18 Sep 2023 15:06:55 -0400 Subject: [PATCH 46/46] Update to latest version of @remix-run/web-fetch (#7477) --- .changeset/bump-web-fetch.md | 5 +++++ packages/remix-node/package.json | 2 +- yarn.lock | 9 +++++---- 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 .changeset/bump-web-fetch.md diff --git a/.changeset/bump-web-fetch.md b/.changeset/bump-web-fetch.md new file mode 100644 index 00000000000..b49dc658008 --- /dev/null +++ b/.changeset/bump-web-fetch.md @@ -0,0 +1,5 @@ +--- +"@remix-run/node": patch +--- + +Update to latest version of `@remix-run/web-fetch` diff --git a/packages/remix-node/package.json b/packages/remix-node/package.json index 91ebce26ace..640293fa4c9 100644 --- a/packages/remix-node/package.json +++ b/packages/remix-node/package.json @@ -18,7 +18,7 @@ ], "dependencies": { "@remix-run/server-runtime": "2.0.0", - "@remix-run/web-fetch": "^4.4.0", + "@remix-run/web-fetch": "^4.4.1", "@remix-run/web-file": "^3.1.0", "@remix-run/web-stream": "^1.1.0", "@web3-storage/multipart-parser": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 2a8b29a40c1..3672952b76f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2166,11 +2166,12 @@ "@remix-run/web-stream" "^1.1.0" web-encoding "1.1.5" -"@remix-run/web-fetch@^4.4.0": - version "4.4.0" - resolved "https://registry.npmjs.org/@remix-run/web-fetch/-/web-fetch-4.4.0.tgz#23919c0f5a6649d133c42e72f2ca08dad18693c1" - integrity sha512-j6vswnGteHd3N5Y2lb5sRKP+NxChVCd1goXRDAanTMFfgxsCV/1ItlJFY8G/F4AESkDydcZ6tz6gAsttkGDiMA== +"@remix-run/web-fetch@^4.4.1": + version "4.4.1" + resolved "https://registry.npmjs.org/@remix-run/web-fetch/-/web-fetch-4.4.1.tgz#1ea34e6f1c660a52e7582007917a552f0efdc58b" + integrity sha512-xMceEGn2kvfeWS91nHSOhEQHPGgjFnmDVpWFZrbWPVdiTByMZIn421/tdSF6Kd1RsNsY+5Iwt3JFEKZHAcMQHw== dependencies: + "@remix-run/web-blob" "^3.1.0" "@remix-run/web-file" "^3.1.0" "@remix-run/web-form-data" "^3.1.0" "@remix-run/web-stream" "^1.1.0"