@shopify/cli-hydrogen 11.1.12 → 11.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@ The Hydrogen extension for the [Shopify CLI](https://shopify.dev/apps/tools/cli)
8
8
 
9
9
  The most common way to test the cli changes locally is to do the following:
10
10
 
11
- - Run `npm run build` in this directory (`packages/cli` from the root of the repo).
11
+ - Run `pnpm run build` in this directory (`packages/cli` from the root of the repo).
12
12
  - Run `npx shopify hydrogen` anywhere else in the monorepo, for example `npx shopify hydrogen init`.
13
13
  - If you want to test a command inside of a template, run the command from within that template or use the `--path` flag to point to another template or any Hydrogen app.
14
14
  - If you want to make changes to a file that is generated when running `npx shopify hydrogen generate`, make changes to that file from inside of the `templates/skeleton` directory.
@@ -1,5 +1,39 @@
1
1
  # skeleton
2
2
 
3
+ ## 2026.4.0
4
+
5
+ ### Major Changes
6
+
7
+ - Update Storefront API and Customer Account API from 2026-01 to 2026-04. ([#3651](https://github.com/Shopify/hydrogen/pull/3651)) by [@itsjustriley](https://github.com/itsjustriley)
8
+
9
+ ## Breaking changes
10
+
11
+ **JSON metafield values limited to 128KB**: When using API version 2026-04 or later, the Storefront API limits JSON type metafield writes to 128KB. This limit applies at the API level - Hydrogen passes through to the Storefront API without additional restriction. Apps that used JSON metafields before April 1, 2026 are grandfathered at the existing 2MB limit. Large metafield values continue to be readable by all API versions.
12
+
13
+ ## New features
14
+
15
+ **New `MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR` cart error code**: Cart operations (`cartCreate`, `cartLinesAdd`, etc.) now return a specific `MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR` error code when a Cart Transform Function fails at runtime, instead of the previous generic `INVALID` error code. If you handle cart errors in your storefront code, you may want to add handling for this new code.
16
+
17
+ ## Changelog links
18
+ - [Storefront API 2026-04 changelog](https://shopify.dev/changelog?filter=api&api_version=2026-04&api_type=storefront-graphql)
19
+ - [Customer Account API 2026-04 changelog](https://shopify.dev/changelog?filter=api&api_version=2026-04&api_type=customer-account-graphql)
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [[`e7215ed0d7a74cacfa8c935f414c1cf32fc2ccd0`](https://github.com/Shopify/hydrogen/commit/e7215ed0d7a74cacfa8c935f414c1cf32fc2ccd0), [`92ab8e8b59807bbdb5dbecaa18629292d5566135`](https://github.com/Shopify/hydrogen/commit/92ab8e8b59807bbdb5dbecaa18629292d5566135), [`b0caa5c013380c7837f049f48da089a1671e2c6d`](https://github.com/Shopify/hydrogen/commit/b0caa5c013380c7837f049f48da089a1671e2c6d)]:
24
+ - @shopify/hydrogen@2026.4.0
25
+
26
+ ## 2026.1.4
27
+
28
+ ### Patch Changes
29
+
30
+ - Remove redundant Storefront API proxy route from skeleton template. The server now automatically proxies requests to `/api/:version/graphql.json` via `createRequestHandler` with `proxyStandardRoutes: true` (enabled by default since December 2025). ([#3572](https://github.com/Shopify/hydrogen/pull/3572)) by [@itsjustriley](https://github.com/itsjustriley)
31
+
32
+ Developers no longer need a manual route file for the tokenless Storefront API. Existing apps with this route can safely delete it - the server-level proxy provides the same functionality with better cookie forwarding and analytics integration.
33
+
34
+ - Updated dependencies [[`b0a75c1d759706931876f056662de2497cb3e688`](https://github.com/Shopify/hydrogen/commit/b0a75c1d759706931876f056662de2497cb3e688), [`a44ee3566b9bb9a7f43f05dfaae6f1f2ab1d548f`](https://github.com/Shopify/hydrogen/commit/a44ee3566b9bb9a7f43f05dfaae6f1f2ab1d548f)]:
35
+ - @shopify/hydrogen@2026.1.4
36
+
3
37
  ## 2026.1.3
4
38
 
5
39
  ### Patch Changes
@@ -2,7 +2,7 @@
2
2
  "name": "skeleton",
3
3
  "private": true,
4
4
  "sideEffects": false,
5
- "version": "2026.1.3",
5
+ "version": "2026.4.0",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "shopify hydrogen build --codegen",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "prettier": "@shopify/prettier-config",
16
16
  "dependencies": {
17
- "@shopify/hydrogen": "2026.1.3",
17
+ "@shopify/hydrogen": "2026.4.0",
18
18
  "graphql": "^16.10.0",
19
19
  "graphql-tag": "^2.12.6",
20
20
  "isbot": "^5.1.22",
@@ -54,7 +54,7 @@
54
54
  "graphql-config": "^5.0.3",
55
55
  "prettier": "^3.4.2",
56
56
  "typescript": "^5.9.2",
57
- "vite": "^6.2.4",
57
+ "vite": "^6.4.2",
58
58
  "vite-tsconfig-paths": "^4.3.1"
59
59
  },
60
60
  "engines": {
@@ -1,6 +1,50 @@
1
1
  import { AbortError } from '@shopify/cli-kit/node/error';
2
2
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
3
3
 
4
+ const KNOWN_USER_ERRORS = [
5
+ {
6
+ pattern: "app is not installed",
7
+ abort: [
8
+ "Hydrogen sales channel isn't installed",
9
+ "Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen"
10
+ ]
11
+ },
12
+ {
13
+ pattern: "Access denied for hydrogenStorefrontCreate field",
14
+ abort: [
15
+ "Couldn't connect storefront to Shopify",
16
+ [
17
+ "Common reasons for this error include:",
18
+ {
19
+ list: {
20
+ items: [
21
+ "The Hydrogen sales channel isn't installed on the store.",
22
+ "You don't have the required account permission to manage apps or channels.",
23
+ "You're trying to connect to an ineligible store type (Trial, Development store)"
24
+ ]
25
+ }
26
+ }
27
+ ]
28
+ ]
29
+ },
30
+ {
31
+ pattern: "Access denied for hydrogenStorefronts field",
32
+ abort: [
33
+ "Couldn't access Hydrogen storefronts",
34
+ [
35
+ "Common reasons for this error include:",
36
+ {
37
+ list: {
38
+ items: [
39
+ "You don't have full access to apps or access to the Hydrogen channel.",
40
+ "The Hydrogen sales channel isn't installed on the store."
41
+ ]
42
+ }
43
+ }
44
+ ]
45
+ ]
46
+ }
47
+ ];
4
48
  async function adminRequest(query, session, variables) {
5
49
  const api = "Admin";
6
50
  const url = `https://${session.storeFqdn}/admin/api/unstable/graphql.json`;
@@ -14,29 +58,10 @@ async function adminRequest(query, session, variables) {
14
58
  });
15
59
  } catch (error) {
16
60
  const errors = error.errors;
17
- if (errors?.some?.((error2) => error2.message.includes("app is not installed"))) {
18
- throw new AbortError(
19
- "Hydrogen sales channel isn't installed",
20
- "Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen"
21
- );
22
- }
23
- if (errors?.some?.(
24
- (error2) => error2.message.includes(
25
- "Access denied for hydrogenStorefrontCreate field"
26
- )
27
- )) {
28
- throw new AbortError("Couldn't connect storefront to Shopify", [
29
- "Common reasons for this error include:",
30
- {
31
- list: {
32
- items: [
33
- "The Hydrogen sales channel isn't installed on the store.",
34
- "You don't have the required account permission to manage apps or channels.",
35
- "You're trying to connect to an ineligible store type (Trial, Development store)"
36
- ]
37
- }
38
- }
39
- ]);
61
+ for (const { pattern, abort } of KNOWN_USER_ERRORS) {
62
+ if (errors?.some?.((e) => e.message.includes(pattern))) {
63
+ throw new AbortError(...abort);
64
+ }
40
65
  }
41
66
  throw error;
42
67
  }
@@ -22,8 +22,7 @@ const ROUTE_MAP = {
22
22
  account: "account*",
23
23
  search: ["search", "api.predictive-search"],
24
24
  robots: "[robots.txt]",
25
- sitemap: ["[sitemap.xml]", "sitemap.$type.$page[.xml]"],
26
- tokenlessApi: "api.$version.[graphql.json]"
25
+ sitemap: ["[sitemap.xml]", "sitemap.$type.$page[.xml]"]
27
26
  };
28
27
  let allRouteTemplateFiles = [];
29
28
  async function getResolvedRoutes(routeKeys = Object.keys(ROUTE_MAP)) {
@@ -0,0 +1,50 @@
1
+ const DEPENDENCY_SECTIONS = [
2
+ "dependencies",
3
+ "devDependencies"
4
+ ];
5
+ function parseCatalogFromWorkspaceYaml(yamlContent) {
6
+ const catalogVersions = {};
7
+ const catalogMatch = yamlContent.match(/^catalog:\s*\n((?:[ \t]+.+\n?)*)/m);
8
+ const catalogSection = catalogMatch?.[1];
9
+ if (!catalogSection) return catalogVersions;
10
+ for (const line of catalogSection.split("\n")) {
11
+ const match = line.match(
12
+ /^\s+['"]?([^'":]+?)['"]?\s*:\s*['"]?([^'"]+?)['"]?\s*$/
13
+ );
14
+ if (match?.[1] && match[2]) {
15
+ catalogVersions[match[1].trim()] = match[2].trim();
16
+ }
17
+ }
18
+ return catalogVersions;
19
+ }
20
+ function parseWorkspacePackagesFromYaml(yamlContent) {
21
+ const packagesMatch = yamlContent.match(
22
+ /^packages:\s*\n((?:[ \t]+-.+\n?)*)/m
23
+ );
24
+ const packagesSection = packagesMatch?.[1];
25
+ if (!packagesSection) return [];
26
+ return packagesSection.split("\n").map((line) => line.match(/^\s+-\s+(.+)/)?.[1]?.trim()).filter((path) => Boolean(path));
27
+ }
28
+ async function resolveWorkspaceProtocols({
29
+ packageJson,
30
+ catalogVersions,
31
+ resolveWorkspaceVersion,
32
+ fallbackVersion
33
+ }) {
34
+ const resolved = JSON.parse(JSON.stringify(packageJson));
35
+ for (const section of DEPENDENCY_SECTIONS) {
36
+ const deps = resolved[section];
37
+ if (!deps) continue;
38
+ for (const [name, version] of Object.entries(deps)) {
39
+ if (version.startsWith("workspace:")) {
40
+ const resolvedVersion = await resolveWorkspaceVersion(name);
41
+ deps[name] = resolvedVersion ?? fallbackVersion;
42
+ } else if (version === "catalog:" && catalogVersions[name]) {
43
+ deps[name] = catalogVersions[name];
44
+ }
45
+ }
46
+ }
47
+ return resolved;
48
+ }
49
+
50
+ export { parseCatalogFromWorkspaceYaml, parseWorkspacePackagesFromYaml, resolveWorkspaceProtocols };
@@ -832,7 +832,7 @@
832
832
  "aliases": [],
833
833
  "args": {
834
834
  "routeName": {
835
- "description": "The route to generate. One of home,page,cart,products,collections,policies,blogs,account,search,robots,sitemap,tokenlessApi,all.",
835
+ "description": "The route to generate. One of home,page,cart,products,collections,policies,blogs,account,search,robots,sitemap,all.",
836
836
  "name": "routeName",
837
837
  "options": [
838
838
  "home",
@@ -846,7 +846,6 @@
846
846
  "search",
847
847
  "robots",
848
848
  "sitemap",
849
- "tokenlessApi",
850
849
  "all"
851
850
  ],
852
851
  "required": true
@@ -1676,5 +1675,5 @@
1676
1675
  ]
1677
1676
  }
1678
1677
  },
1679
- "version": "11.1.12"
1678
+ "version": "11.1.14"
1680
1679
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "@shopify:registry": "https://registry.npmjs.org"
6
6
  },
7
- "version": "11.1.12",
7
+ "version": "11.1.14",
8
8
  "license": "MIT",
9
9
  "type": "module",
10
10
  "repository": {
@@ -16,7 +16,7 @@
16
16
  "@react-router/dev": "7.12.0",
17
17
  "@types/diff": "^5.0.2",
18
18
  "@types/gunzip-maybe": "^1.4.0",
19
- "@types/node": "^22",
19
+ "@types/node": "22.19.15",
20
20
  "esbuild": "^0.25.0",
21
21
  "@types/prettier": "^2.7.2",
22
22
  "@types/source-map-support": "^0.5.10",
@@ -27,7 +27,7 @@
27
27
  "flame-chart-js": "2.3.2",
28
28
  "get-port": "^7.0.0",
29
29
  "type-fest": "^4.33.0",
30
- "vite": "^6.2.4",
30
+ "vite": "^6.4.2",
31
31
  "vitest": "^1.0.4"
32
32
  },
33
33
  "dependencies": {
@@ -55,11 +55,11 @@
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@graphql-codegen/cli": "^5.0.2",
58
- "@react-router/dev": "7.12.0",
58
+ "@react-router/dev": "^7.12.0",
59
59
  "graphql-config": "^5.0.3",
60
60
  "vite": "^5.1.0 || ^6.2.0",
61
- "@shopify/mini-oxygen": "4.0.2",
62
- "@shopify/hydrogen-codegen": "0.3.3"
61
+ "@shopify/hydrogen-codegen": "0.3.3",
62
+ "@shopify/mini-oxygen": "4.0.2"
63
63
  },
64
64
  "peerDependenciesMeta": {
65
65
  "@graphql-codegen/cli": {
@@ -1,14 +0,0 @@
1
- import type {Route} from './+types/api.$version.[graphql.json]';
2
-
3
- export async function action({params, context, request}: Route.ActionArgs) {
4
- const response = await fetch(
5
- `https://${context.env.PUBLIC_CHECKOUT_DOMAIN}/api/${params.version}/graphql.json`,
6
- {
7
- method: 'POST',
8
- body: request.body,
9
- headers: request.headers,
10
- },
11
- );
12
-
13
- return new Response(response.body, {headers: new Headers(response.headers)});
14
- }