diff --git a/.api-reports/api-report-utilities_globals.md b/.api-reports/api-report-utilities_globals.md index 2383be3ea83..cf51100b464 100644 --- a/.api-reports/api-report-utilities_globals.md +++ b/.api-reports/api-report-utilities_globals.md @@ -44,7 +44,7 @@ type WrappedInvariant = { // Warnings were encountered during analysis: // -// src/utilities/globals/invariantWrappers.ts:58:3 - (ae-forgotten-export) The symbol "LogFunction" needs to be exported by the entry point index.d.ts +// src/utilities/globals/invariantWrappers.ts:62:3 - (ae-forgotten-export) The symbol "LogFunction" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/.changeset/early-snakes-cheer.md b/.changeset/early-snakes-cheer.md deleted file mode 100644 index 0545ac8cbdc..00000000000 --- a/.changeset/early-snakes-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@apollo/client": patch ---- - -Fix nextFetchPolicy behaviour with transformed documents by keeping `options` reference stable when passing it through QueryManager. diff --git a/.changeset/hungry-vans-walk.md b/.changeset/hungry-vans-walk.md deleted file mode 100644 index 733ff354579..00000000000 --- a/.changeset/hungry-vans-walk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@apollo/client": patch ---- - -Fixes a race condition in asyncMap that caused issues in React Native when errors were returned in the response payload along with a data property that was null. diff --git a/.changeset/pretty-readers-lick.md b/.changeset/pretty-readers-lick.md deleted file mode 100644 index 9811ccb33fa..00000000000 --- a/.changeset/pretty-readers-lick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@apollo/client": patch ---- - -Remove (already throwing) SuspenseCache export that should have been removed in 3.8. diff --git a/.changeset/short-owls-breathe.md b/.changeset/short-owls-breathe.md deleted file mode 100644 index 352eb2f6409..00000000000 --- a/.changeset/short-owls-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@apollo/client": patch ---- - -Remove some dead code. diff --git a/.circleci/config.yml b/.circleci/config.yml index 4743a55f95e..8ab65c744a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ jobs: Lint: docker: - - image: cimg/node:20.6.1 + - image: cimg/node:20.7.0 steps: - checkout - run: npm version @@ -21,7 +21,7 @@ jobs: Formatting: docker: - - image: cimg/node:20.6.1 + - image: cimg/node:20.7.0 steps: - checkout - run: npm ci @@ -29,7 +29,7 @@ jobs: Tests: docker: - - image: cimg/node:20.6.1 + - image: cimg/node:20.7.0 steps: - checkout - run: npm run ci:precheck @@ -47,7 +47,7 @@ jobs: BuildTarball: docker: - - image: cimg/node:20.6.1 + - image: cimg/node:20.7.0 steps: - checkout - run: npm run ci:precheck @@ -64,7 +64,7 @@ jobs: framework: type: string docker: - - image: cimg/node:20.6.1 + - image: cimg/node:20.7.0 steps: - checkout - attach_workspace: diff --git a/.size-limit.cjs b/.size-limit.cjs index b3ed1cc372c..d8da5ff2578 100644 --- a/.size-limit.cjs +++ b/.size-limit.cjs @@ -1,7 +1,7 @@ const checks = [ { path: "dist/apollo-client.min.cjs", - limit: "37914", + limit: "37940", }, { path: "dist/main.cjs", @@ -10,7 +10,7 @@ const checks = [ { path: "dist/index.js", import: "{ ApolloClient, InMemoryCache, HttpLink }", - limit: "31947", + limit: "31970", }, ...[ "ApolloProvider", diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd66f5496f..22d3ee42b53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # @apollo/client +## 3.8.5 + +### Patch Changes + +- [#11266](https://github.com/apollographql/apollo-client/pull/11266) [`5192cf6e1`](https://github.com/apollographql/apollo-client/commit/5192cf6e1e958080bcae09e5967fa6851bd3a78c) Thanks [@phryneas](https://github.com/phryneas)! - Fixes argument handling for invariant log messages. + +- [#11235](https://github.com/apollographql/apollo-client/pull/11235) [`6cddaaf65`](https://github.com/apollographql/apollo-client/commit/6cddaaf6543f5c0b1fb04ba47480fb393ba10de7) Thanks [@phryneas](https://github.com/phryneas)! - Fix nextFetchPolicy behaviour with transformed documents by keeping `options` reference stable when passing it through QueryManager. + +- [#11252](https://github.com/apollographql/apollo-client/pull/11252) [`327a2abbd`](https://github.com/apollographql/apollo-client/commit/327a2abbd5db87ca27f2ffd1d2f8dccd75868a58) Thanks [@phryneas](https://github.com/phryneas)! - Fixes a race condition in asyncMap that caused issues in React Native when errors were returned in the response payload along with a data property that was null. + +- [#11229](https://github.com/apollographql/apollo-client/pull/11229) [`c372bad4e`](https://github.com/apollographql/apollo-client/commit/c372bad4ebd01a4f2e772cd76e873143bf043fe6) Thanks [@phryneas](https://github.com/phryneas)! - Remove (already throwing) SuspenseCache export that should have been removed in 3.8. + +- [#11267](https://github.com/apollographql/apollo-client/pull/11267) [`bc055e068`](https://github.com/apollographql/apollo-client/commit/bc055e0683e87b9445e321f73857f4a91b20a9ce) Thanks [@phryneas](https://github.com/phryneas)! - Remove some dead code. + ## 3.8.4 ### Patch Changes diff --git a/package-lock.json b/package-lock.json index 6bed6ba174f..387d3923ac2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@apollo/client", - "version": "3.8.4", + "version": "3.8.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@apollo/client", - "version": "3.8.4", + "version": "3.8.5", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -30,7 +30,7 @@ "@changesets/changelog-github": "0.4.8", "@changesets/cli": "2.26.2", "@graphql-tools/schema": "10.0.0", - "@microsoft/api-extractor": "7.37.0", + "@microsoft/api-extractor": "7.38.0", "@rollup/plugin-node-resolve": "11.2.1", "@size-limit/esbuild-why": "8.2.6", "@size-limit/preset-small-lib": "8.2.6", @@ -2111,17 +2111,17 @@ } }, "node_modules/@microsoft/api-extractor": { - "version": "7.37.0", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.37.0.tgz", - "integrity": "sha512-df/wffWpDhYRw7kzdxeHGsCpim+dC8aFiZlsJb4uFvVPWhBZpDzOhQxSUTFx3Df1ORY+/JjuPR3fDE9Hq+PHzQ==", + "version": "7.38.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.38.0.tgz", + "integrity": "sha512-e1LhZYnfw+JEebuY2bzhw0imDCl1nwjSThTrQqBXl40hrVo6xm3j/1EpUr89QyzgjqmAwek2ZkIVZbrhaR+cqg==", "dev": true, "dependencies": { - "@microsoft/api-extractor-model": "7.28.0", + "@microsoft/api-extractor-model": "7.28.2", "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.60.0", - "@rushstack/rig-package": "0.5.0", - "@rushstack/ts-command-line": "4.16.0", + "@rushstack/node-core-library": "3.61.0", + "@rushstack/rig-package": "0.5.1", + "@rushstack/ts-command-line": "4.16.1", "colors": "~1.2.1", "lodash": "~4.17.15", "resolve": "~1.22.1", @@ -2134,14 +2134,14 @@ } }, "node_modules/@microsoft/api-extractor-model": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.0.tgz", - "integrity": "sha512-QIMtUVm1tqiKG+M6ciFgRShcDoovyltaeg+CbyOnyr7SMrp6gg0ojK5/nToMqR9kAvsTS4QVgW4Twl50EoAjcw==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.2.tgz", + "integrity": "sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==", "dev": true, "dependencies": { "@microsoft/tsdoc": "0.14.2", "@microsoft/tsdoc-config": "~0.16.1", - "@rushstack/node-core-library": "3.60.0" + "@rushstack/node-core-library": "3.61.0" } }, "node_modules/@microsoft/api-extractor/node_modules/semver": { @@ -2292,9 +2292,9 @@ "dev": true }, "node_modules/@rushstack/node-core-library": { - "version": "3.60.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.60.0.tgz", - "integrity": "sha512-PcyrqhILvzU+65wMFybQ2VeGNnU5JzhDq2OvUi3j6jPUxyllM7b2hrRUwCuVaYboewYzIbpzXFzgxe2K7ii1nw==", + "version": "3.61.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.61.0.tgz", + "integrity": "sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==", "dev": true, "dependencies": { "colors": "~1.2.1", @@ -2330,9 +2330,9 @@ } }, "node_modules/@rushstack/rig-package": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.0.tgz", - "integrity": "sha512-bGnOW4DWHOePDiABKy6qyqYJl9i7fKn4bRucExRVt5QzyPxuVHMl8CMmCabtoNSpXzgG3qymWOrMoa/W2PpJrw==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.1.tgz", + "integrity": "sha512-pXRYSe29TjRw7rqxD4WS3HN/sRSbfr+tJs4a9uuaSIBAITbUggygdhuG0VrO0EO+QqH91GhYMN4S6KRtOEmGVA==", "dev": true, "dependencies": { "resolve": "~1.22.1", @@ -2340,9 +2340,9 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.16.0.tgz", - "integrity": "sha512-WJKhdR9ThK9Iy7t78O3at7I3X4Ssp5RRZay/IQa8NywqkFy/DQbT3iLouodMMdUwLZD9n8n++xLubVd3dkmpkg==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.16.1.tgz", + "integrity": "sha512-+OCsD553GYVLEmz12yiFjMOzuPeCiZ3f8wTiFHL30ZVXexTyPmgjwXEhg2K2P0a2lVf+8YBy7WtPoflB2Fp8/A==", "dev": true, "dependencies": { "@types/argparse": "1.0.38", diff --git a/package.json b/package.json index 17e136e68d0..1b44347fd69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@apollo/client", - "version": "3.8.4", + "version": "3.8.5", "description": "A fully-featured caching GraphQL client.", "private": true, "keywords": [ @@ -108,7 +108,7 @@ "@changesets/changelog-github": "0.4.8", "@changesets/cli": "2.26.2", "@graphql-tools/schema": "10.0.0", - "@microsoft/api-extractor": "7.37.0", + "@microsoft/api-extractor": "7.38.0", "@rollup/plugin-node-resolve": "11.2.1", "@size-limit/esbuild-why": "8.2.6", "@size-limit/preset-small-lib": "8.2.6", diff --git a/src/dev/loadErrorMessageHandler.ts b/src/dev/loadErrorMessageHandler.ts index 6dc7743ef2c..199a6477b5c 100644 --- a/src/dev/loadErrorMessageHandler.ts +++ b/src/dev/loadErrorMessageHandler.ts @@ -20,7 +20,7 @@ export function loadErrorMessageHandler(...errorCodes: ErrorCodes[]) { message = definition.message; } return args.reduce( - (msg, arg) => msg.replace("%s", String(arg)), + (msg, arg) => msg.replace(/%[sdfo]/, String(arg)), String(message) ); } diff --git a/src/utilities/globals/__tests__/invariantWrappers.test.ts b/src/utilities/globals/__tests__/invariantWrappers.test.ts new file mode 100644 index 00000000000..f9255c170c8 --- /dev/null +++ b/src/utilities/globals/__tests__/invariantWrappers.test.ts @@ -0,0 +1,101 @@ +import { loadErrorMessageHandler } from "../../../dev"; +import { spyOnConsole, withCleanup } from "../../../testing/internal"; +import { + ApolloErrorMessageHandler, + InvariantError, + invariant, +} from "../invariantWrappers"; + +function disableErrorMessageHandler() { + const original = window[ApolloErrorMessageHandler]; + delete window[ApolloErrorMessageHandler]; + return withCleanup({ original }, ({ original }) => { + window[ApolloErrorMessageHandler] = original; + }); +} + +function mockErrorMessageHandler() { + const original = window[ApolloErrorMessageHandler]; + delete window[ApolloErrorMessageHandler]; + + loadErrorMessageHandler({ + 5: { file: "foo", message: "Replacing %s, %d, %f, %o" }, + }); + + return withCleanup({ original }, ({ original }) => { + window[ApolloErrorMessageHandler] = original; + }); +} + +test("base invariant(false, 5, ...), no handlers", () => { + using _ = disableErrorMessageHandler(); + expect(() => { + invariant(false, 5, "string", 1, 1.1, { a: 1 }); + }).toThrow( + new InvariantError( + "An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#" + + encodeURIComponent( + JSON.stringify({ + version: "local", + message: 5, + args: [ + "string", + "1", + "1.1", + JSON.stringify({ a: 1 }, undefined, 2), + ], + }) + ) + ) + ); +}); + +test("base invariant(false, 5, ...), handlers in place", () => { + using _ = mockErrorMessageHandler(); + expect(() => { + invariant(false, 5, "string", 1, 1.1, { a: 1 }); + }).toThrow(new InvariantError('Replacing string, 1, 1.1, {\n "a": 1\n}')); +}); + +test("base invariant(false, undefined), no handlers", () => { + using _ = disableErrorMessageHandler(); + expect(() => { + invariant(false); + }).toThrow(new InvariantError("Invariant Violation")); +}); + +test("base invariant(false, undefined), handlers in place", () => { + using _ = mockErrorMessageHandler(); + expect(() => { + invariant(false); + }).toThrow(new InvariantError("Invariant Violation")); +}); + +test("invariant.log(5, ...), no handlers", () => { + using _ = disableErrorMessageHandler(); + using consoleSpy = spyOnConsole("log"); + invariant.log(5, "string", 1, 1.1, { a: 1 }); + expect(consoleSpy.log).toHaveBeenCalledWith( + "An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#" + + encodeURIComponent( + JSON.stringify({ + version: "local", + message: 5, + args: ["string", "1", "1.1", JSON.stringify({ a: 1 }, undefined, 2)], + }) + ) + ); +}); + +test("invariant.log(5, ...), with handlers", () => { + using _ = mockErrorMessageHandler(); + using consoleSpy = spyOnConsole("log"); + invariant.log(5, "string", 1, 1.1, { a: 1 }); + expect(consoleSpy.log).toHaveBeenCalledWith( + "Replacing %s, %d, %f, %o", + "string", + 1, + 1.1, + { a: 1 } + ); +}); diff --git a/src/utilities/globals/invariantWrappers.ts b/src/utilities/globals/invariantWrappers.ts index 74d83f3bd34..adcbea0ad01 100644 --- a/src/utilities/globals/invariantWrappers.ts +++ b/src/utilities/globals/invariantWrappers.ts @@ -5,12 +5,16 @@ import type { ErrorCodes } from "../../invariantErrorCodes.js"; import { stringifyForDisplay } from "../common/stringifyForDisplay.js"; function wrap(fn: (msg?: string, ...args: any[]) => void) { - return function (message: string | number, ...args: any[]) { + return function (message?: string | number, ...args: any[]) { if (typeof message === "number") { - fn(getErrorMsg(message, args)); - } else { - fn(message, ...args); + const arg0 = message; + message = getHandledErrorMsg(arg0); + if (!message) { + message = getFallbackErrorMsg(arg0, args); + args = []; + } } + fn(...[message].concat(args)); }; } @@ -67,7 +71,10 @@ const invariant: WrappedInvariant = Object.assign( ...args: unknown[] ): asserts condition { if (!condition) { - originalInvariant(condition, getErrorMsg(message, args)); + originalInvariant( + condition, + getHandledErrorMsg(message, args) || getFallbackErrorMsg(message, args) + ); } }, { @@ -92,7 +99,10 @@ function newInvariantError( message?: string | number, ...optionalParams: unknown[] ) { - return new InvariantError(getErrorMsg(message, optionalParams)); + return new InvariantError( + getHandledErrorMsg(message, optionalParams) || + getFallbackErrorMsg(message, optionalParams) + ); } const ApolloErrorMessageHandler = Symbol.for( @@ -106,24 +116,37 @@ declare global { } } -function getErrorMsg(message?: string | number, messageArgs: unknown[] = []) { +function stringify(arg: any) { + return typeof arg == "string" + ? arg + : stringifyForDisplay(arg, 2).slice(0, 1000); +} + +function getHandledErrorMsg( + message?: string | number, + messageArgs: unknown[] = [] +) { if (!message) return; - const args = messageArgs.map((arg) => - typeof arg == "string" ? arg : stringifyForDisplay(arg, 2).slice(0, 1000) - ); return ( - (global[ApolloErrorMessageHandler] && - global[ApolloErrorMessageHandler](message, args)) || - `An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#${encodeURIComponent( - JSON.stringify({ - version, - message, - args, - }) - )}` + global[ApolloErrorMessageHandler] && + global[ApolloErrorMessageHandler](message, messageArgs.map(stringify)) ); } +function getFallbackErrorMsg( + message?: string | number, + messageArgs: unknown[] = [] +) { + if (!message) return; + return `An error occurred! For more details, see the full error text at https://go.apollo.dev/c/err#${encodeURIComponent( + JSON.stringify({ + version, + message, + args: messageArgs.map(stringify), + }) + )}`; +} + export { invariant, InvariantError,