Skip to content
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e4444a2
fix(core): add addition cookie options to anonymous cookie deletion (…
chanceaclark Jun 27, 2025
50afc7c
chore: fix changesets dependency resolution (#2454)
chanceaclark Jun 30, 2025
490e683
docs: update .env.test.example with api account scope reqs (#2456)
matthewvolk Jun 30, 2025
47c4253
Version Packages (`canary`) (#2452)
github-actions[bot] Jul 1, 2025
a438bb6
Bump next to latest canary (#2465)
bookernath Jul 6, 2025
bc96a8a
docs: how to verify new merge base established when syncing (#2451)
matthewvolk Jul 7, 2025
474f960
chore(core): remove runtime edge declarations (#2464)
jorgemoya Jul 8, 2025
8b64931
Update translations (#2468)
bc-svc-local Jul 8, 2025
989bf97
Use min and max purchase quantity to set bounds of quantity number in…
bookernath Jul 10, 2025
7a1ca71
chore: update ci to include @bigcommerce/catalyst (#2463)
matthewvolk Jul 14, 2025
dc28a03
chore: update deps mostly related to packages/cli (#2461)
matthewvolk Jul 14, 2025
6de33ab
chore: use @commander-js/extra-typings as devDep (#2462)
matthewvolk Jul 14, 2025
02af32c
feat: render scripts from Script Manager (#2477)
bookernath Jul 14, 2025
df0af51
chore: hoist eslint plugins for ide support (#2460)
matthewvolk Jul 16, 2025
778bd21
feat: recursively copy cwd into temp folder (#2479)
matthewvolk Jul 16, 2025
d0acf47
feat(cli): scaffold deploy command and generate bundle zip (#2481)
jorgemoya Jul 17, 2025
7fb090a
feat: install and build all deps required to bundle catalyst (#2486)
matthewvolk Jul 21, 2025
4b40859
chore: bump opennextjs-cloudflare version (#2487)
matthewvolk Jul 21, 2025
66234cc
feat(cli): generate upload signature and upload bundle.zip (#2484)
jorgemoya Jul 21, 2025
b7798c2
refactor: infer option types in deploy command action (#2490)
matthewvolk Jul 22, 2025
5f58f7c
feat: copy templates from cli to catalyst build tmpdir (#2489)
matthewvolk Jul 22, 2025
cbaf146
Version Packages (`canary`) (#2466)
github-actions[bot] Jul 23, 2025
f7daa8e
Merge branch 'canary' into sync-integrations-makeswift
matthewvolk Jul 23, 2025
545bbdc
chore: update codeowners for integrations/makeswift
matthewvolk Jul 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions .github/workflows/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
node-version-file: ".nvmrc"
cache: "pnpm"

- name: Install dependencies
run: pnpm install --frozen-lockfile
Expand All @@ -48,10 +48,13 @@ jobs:
- name: Typecheck
run: pnpm run typecheck

unit-tests:
name: CLI Unit Tests
cli-tests:
name: CLI Tests

runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}

steps:
- name: Checkout code
Expand All @@ -71,5 +74,4 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Run Tests
run: pnpm test
working-directory: packages/create-catalyst
run: pnpm run test
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ To pull the latest code from `canary` into `integrations/makeswift`, follow the
```

> [!IMPORTANT]
> It is very important that you do not use the merge button in the GitHub UI to merge your PR. The problem with the "Squash and merge" or "Rebase and merge" button in GitHub is that it **rewrites history and throws away your local merge commit**. Instead, locally rebasing your `integrations/makeswift` branch onto your `{new-branch-name}` branch preserves the merge commit from step 4, which will properly set the new merge base for future merges from `canary` into `integrations/makeswift`.
> We have added a GitHub Ruleset to protect against this, but it's worth explicitly documenting here for posterity: It is very important that we do not "Squash and merge" or "Rebase and merge" our changes onto `integrations/makeswift`. Instead, we should either merge the PR with a traditional merge commit (the button in the GitHub PR UI should say "Merge pull request"), or locally rebase the `integrations/makeswift` branch onto the `{new-branch-name}` branch (as illustrated in the step above). Either of these options will correctly preserve the merge commit from step 4 in the history of the `integrations/makeswift` branch, which will then set the new merge base for future merges from `canary` into `integrations/makeswift`.
>
> If you are unsure whether or not you've done this correctly, you can run `git merge canary` from `integrations/makeswift` after rebasing in the step above; if you see "Already up to date.", you followed the steps correctly (with one caveat: in the case that new commits have been pushed to `canary` since the last time you merged, then you may see a new merge commit/potential conflicts for only those new commits).

7. Push the changes up to GitHub, which will automatically close the open PR from step 5.

Expand Down
20 changes: 19 additions & 1 deletion core/.env.test.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# For example, if BIGCOMMERCE_CHANNEL_ID=1 in .env.local, but BIGCOMMERCE_CHANNEL_ID=5 in this file, the test environment will use BIGCOMMERCE_CHANNEL_ID=5

# Access token client ID and client secret are required for JWT login tests. If not provided, tests that require these will be skipped.
# If provided, ensure the API account associated with this client ID/secret pair has the following scope:
# - Customers login: login
BIGCOMMERCE_CLIENT_ID=
BIGCOMMERCE_CLIENT_SECRET=

Expand All @@ -28,7 +30,23 @@ TEST_CUSTOMER_ID=1
TEST_CUSTOMER_EMAIL=example@test.com
TEST_CUSTOMER_PASSWORD=Password1

# Used to create customer accounts for testing purposes if no existing customer account is provided
# Used to create/update/delete store data for testing purposes
# Some tests will not pass without a BIGCOMMERCE_ACCESS_TOKEN, even with TESTS_READ_ONLY set to true
# This is due to the test API client sending GET (read-only) API requests
# Create a token with the following scopes:
# - Content: read-only
# - Products: read-only
# - Channel listings: read-only
# - Channel settings: read-only
# - Information & settings: read-only
# - Customers: read-only
# - Orders: read-only
# When TESTS_READ_ONLY is false, you'll need additional scopes:
# - Products: modify
# - Content: modify
# - Customers: modify
# - Marketing: modify
# - Orders: modify
BIGCOMMERCE_ACCESS_TOKEN=

# The default product ID to use for tests. This needs to be a product without variants (simple)
Expand Down
2 changes: 1 addition & 1 deletion core/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

require('@bigcommerce/eslint-config/patch');

/** @type {import('eslint').Linter.Config} */
/** @type {import('eslint').Linter.LegacyConfig} */
const config = {
root: true,
extends: [
Expand Down
26 changes: 22 additions & 4 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 1.1.0

### Minor Changes

- [#2477](https://github.com/bigcommerce/catalyst/pull/2477) [`02af32c`](https://github.com/bigcommerce/catalyst/commit/02af32c459719f97e8973a19b6889e5fa73d0c38) Thanks [@bookernath](https://github.com/bookernath)! - Add support for Scripts API/Script Manager scripts rendering via next/script

### Patch Changes

- [#2465](https://github.com/bigcommerce/catalyst/pull/2465) [`a438bb6`](https://github.com/bigcommerce/catalyst/commit/a438bb660bc3bd11adacd125769ba99ba2e1c38d) Thanks [@bookernath](https://github.com/bookernath)! - Bump next to 15.4.0-canary.114 to fix issue with PDPs 500ing on Docker builds

- [#2474](https://github.com/bigcommerce/catalyst/pull/2474) [`989bf97`](https://github.com/bigcommerce/catalyst/commit/989bf974c534a7201782ace9a4bf3fe745e8af01) Thanks [@bookernath](https://github.com/bookernath)! - Respect min/max purchase quantity from API in quantity selector

- [#2464](https://github.com/bigcommerce/catalyst/pull/2464) [`474f960`](https://github.com/bigcommerce/catalyst/commit/474f960c4c428e28874022b36ae2b03e0b301e20) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Remove edge runtime declarations to be able to run Catalyst with OpenNext.

- [#2468](https://github.com/bigcommerce/catalyst/pull/2468) [`8b64931`](https://github.com/bigcommerce/catalyst/commit/8b6493156a70490c0c35c35d45ebd9ad8f23615c) Thanks [@bc-svc-local](https://github.com/bc-svc-local)! - Update translations.

## 1.0.1

### Patch Changes

- [#2448](https://github.com/bigcommerce/catalyst/pull/2448) [`e4444a2`](https://github.com/bigcommerce/catalyst/commit/e4444a2ca83b5b73776c842feff56e47f57344dc) Thanks [@chanceaclark](https://github.com/chanceaclark)! - Fixes an issue where the anonymous session wasn't getting cleared after an actual session was established.

## 1.0.0

### Major Changes
Expand Down Expand Up @@ -189,7 +211,6 @@
### Minor Changes

- [#1491](https://github.com/bigcommerce/catalyst/pull/1491) [`313a591`](https://github.com/bigcommerce/catalyst/commit/313a5913181a144b53cb12208132f4a9924e2256) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Bump `next-intl` which includes [some minor changes and updated APIs](<(https://next-intl-docs.vercel.app/blog/next-intl-3-22)>):

- Use new `createNavigation` api.
- Pass `locale` to redirects.
- `setRequestLocale` is no longer unstable.
Expand Down Expand Up @@ -285,7 +306,6 @@
- [#1362](https://github.com/bigcommerce/catalyst/pull/1362) [`0814afe`](https://github.com/bigcommerce/catalyst/commit/0814afefca00b2497dddb0622df45f4d50865882) Thanks [@deini](https://github.com/deini)! - If app is not running on Vercel's infra, `<Analytics />` and `<SpeedInsights />` are not rendered.

Opt-out of vercel analytics and speed insights by setting the following env vars to `true`

- `DISABLE_VERCEL_ANALYTICS`
- `DISABLE_VERCEL_SPEED_INSIGHTS`

Expand Down Expand Up @@ -374,7 +394,6 @@
- [#1278](https://github.com/bigcommerce/catalyst/pull/1278) [`f8553c6`](https://github.com/bigcommerce/catalyst/commit/f8553c6c9fb35ab7a143fabd60719c8156269448) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Fix wrapping author text in BlogPostCard.

- [#1322](https://github.com/bigcommerce/catalyst/pull/1322) [`77ecb4b`](https://github.com/bigcommerce/catalyst/commit/77ecb4bb4f527e079788b0f9dff2468e92d0bc1a) Thanks [@jorgemoya](https://github.com/jorgemoya)! - Split auth forms to four different pages:

- /login
- /register
- /reset
Expand Down Expand Up @@ -424,7 +443,6 @@
- [#1194](https://github.com/bigcommerce/catalyst/pull/1194) [`b455b05`](https://github.com/bigcommerce/catalyst/commit/b455b05a6121b005bd5147a25c964b9554b1b350) Thanks [@BC-krasnoshapka](https://github.com/BC-krasnoshapka)! - Add basic support for Google Analytics via [Big Open Data Layer](https://developer.bigcommerce.com/docs/integrations/hosted-analytics). BODL and GA4 integration is encapsulated in `bodl` library which hides current complexity and limitations that will be improved in future. It can be extended with more events and integrations with other analytics providers later. Data transformation from Catalyst data models to BODL and firing events is done in client components, as only frontend events are supported by BODL for now.

List of currently supported events:

- View product category
- View product page
- Add product to cart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,4 @@ export async function GET(_: Request, { params }: { params: Promise<{ token: str
}
}

export const runtime = 'edge';
export const dynamic = 'force-dynamic';
2 changes: 2 additions & 0 deletions core/app/[locale]/(default)/product/[slug]/page-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ const StreamableProductQuery = graphql(
}
}
}
minPurchaseQuantity
maxPurchaseQuantity
warranty
inventory {
isInStock
Expand Down
14 changes: 14 additions & 0 deletions core/app/[locale]/(default)/product/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,18 @@ export default async function Product({ params, searchParams }: Props) {
return productCardTransformer(relatedProducts, format);
});

const streamableMinQuantity = Streamable.from(async () => {
const product = await streamableProduct;

return product.minPurchaseQuantity;
});

const streamableMaxQuantity = Streamable.from(async () => {
const product = await streamableProduct;

return product.maxPurchaseQuantity;
});

const streamableAnalyticsData = Streamable.from(async () => {
const [extendedProduct, pricingProduct] = await Streamable.all([
streamableProduct,
Expand Down Expand Up @@ -313,6 +325,8 @@ export default async function Product({ params, searchParams }: Props) {
subtitle: baseProduct.brand?.name,
rating: baseProduct.reviewSummary.averageRating,
accordions: streameableAccordions,
minQuantity: streamableMinQuantity,
maxQuantity: streamableMaxQuantity,
}}
productId={baseProduct.entityId}
quantityLabel={t('ProductDetails.quantity')}
Expand Down
11 changes: 10 additions & 1 deletion core/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { revalidate } from '~/client/revalidate-target';
import { WebAnalyticsFragment } from '~/components/analytics/fragment';
import { AnalyticsProvider } from '~/components/analytics/provider';
import { ContainerQueryPolyfill } from '~/components/polyfills/container-query';
import { ScriptManagerScripts, ScriptsFragment } from '~/components/scripts';
import { routing } from '~/i18n/routing';
import { SiteTheme } from '~/lib/makeswift/components/site-theme';
import { MakeswiftProvider } from '~/lib/makeswift/provider';
Expand All @@ -41,13 +42,16 @@ const RootLayoutMetadataQuery = graphql(
}
...WebAnalyticsFragment
}
content {
...ScriptsFragment
}
}
channel {
entityId
}
}
`,
[WebAnalyticsFragment],
[WebAnalyticsFragment, ScriptsFragment],
);

const fetchRootLayoutMetadata = cache(async () => {
Expand Down Expand Up @@ -118,6 +122,10 @@ export default async function RootLayout({ params, children }: Props) {
<html className={clsx(fonts.map((f) => f.variable))} lang={locale}>
<head>
<SiteTheme />
<ScriptManagerScripts
scripts={data.site.content.headerScripts}
strategy="afterInteractive"
/>
</head>
<body className="flex min-h-screen flex-col">
<NextIntlClientProvider>
Expand All @@ -134,6 +142,7 @@ export default async function RootLayout({ params, children }: Props) {
</NextIntlClientProvider>
<VercelComponents />
<ContainerQueryPolyfill />
<ScriptManagerScripts scripts={data.site.content.footerScripts} strategy="lazyOnload" />
</body>
</html>
</MakeswiftProvider>
Expand Down
2 changes: 0 additions & 2 deletions core/app/admin/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,3 @@ export const GET = () => {
locale: defaultLocale,
});
};

export const runtime = 'edge';
1 change: 0 additions & 1 deletion core/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { handlers } from '~/auth';

export const { GET, POST } = handlers;
export const runtime = 'edge';
2 changes: 0 additions & 2 deletions core/app/sitemap.xml/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,3 @@ export const GET = async () => {
},
});
};

export const runtime = 'edge';
2 changes: 0 additions & 2 deletions core/app/xmlsitemap.php/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ import { permanentRedirect } from '~/i18n/routing';
export const GET = () => {
permanentRedirect({ href: '/sitemap.xml', locale: defaultLocale });
};

export const runtime = 'edge';
7 changes: 6 additions & 1 deletion core/auth/anonymous-session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ export const clearAnonymousSession = async () => {
const useSecureCookies = await shouldUseSecureCookie();
const cookiePrefix = useSecureCookies ? '__Secure-' : '';

cookieJar.delete(`${cookiePrefix}${anonymousCookieName}`);
cookieJar.delete({
name: `${cookiePrefix}${anonymousCookieName}`,
secure: true,
sameSite: 'lax',
httpOnly: true,
});
};

export const updateAnonymousSession = async (user: AnonymousUser) => {
Expand Down
1 change: 1 addition & 0 deletions core/client/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const graphql = initGraphQLTada<{
DateTime: string;
Long: number;
BigDecimal: number;
UUID: string;
};
disableMasking: true;
}>();
Expand Down
66 changes: 66 additions & 0 deletions core/components/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Script Components

This directory contains components for rendering BigCommerce scripts using Next.js Script component.

## Components

### ScriptManagerScripts
A single component that renders scripts with configurable strategy. Location filtering is now handled at the GraphQL level, simplifying the component logic.

## Usage

```tsx
import { ScriptManagerScripts } from '~/components/scripts';

// In your layout component:
<head>
<ScriptManagerScripts
scripts={data.site.content.headerScripts}
strategy="afterInteractive"
/>
</head>
<body>
{children}
<ScriptManagerScripts
scripts={data.site.content.footerScripts}
strategy="lazyOnload"
/>
</body>
```

## Architecture

The component accepts:
- `scripts`: Pre-filtered GraphQL scripts data (headerScripts or footerScripts)
- `strategy`: 'afterInteractive', 'lazyOnload', or other Next.js Script strategies

## Script Processing

The component handles both types of BigCommerce scripts:

### External Scripts (SrcScript)
```tsx
<Script
id="bc-script-123"
src="https://example.com/script.js"
strategy="afterInteractive"
integrity="sha256-abc123..."
crossOrigin="anonymous"
/>
```

### Inline Scripts (InlineScript)
Inline scripts are rendered using `dangerouslySetInnerHTML` for reliable execution:
```tsx
<Script
id="bc-script-456"
strategy="lazyOnload"
dangerouslySetInnerHTML={{ __html: scriptContent }}
/>
```

## Performance & Security

- Header scripts use `afterInteractive` strategy for critical functionality
- Footer scripts use `lazyOnload` strategy for better performance
- Integrity hashes are included when available for security
39 changes: 39 additions & 0 deletions core/components/scripts/fragment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { graphql } from '~/client/graphql';

export const ScriptsFragment = graphql(`
fragment ScriptsFragment on Content {
headerScripts: scripts(
first: 50
filters: { visibilities: [ALL_PAGES, STOREFRONT], location: HEAD }
) {
...ScriptTypeConnectionFragment
}
footerScripts: scripts(
first: 50
filters: { visibilities: [ALL_PAGES, STOREFRONT], location: FOOTER }
) {
...ScriptTypeConnectionFragment
}
}

fragment ScriptTypeConnectionFragment on ScriptTypeConnection {
edges {
node {
__typename
integrityHashes {
hash
}
entityId
consentCategory
location
visibility
... on InlineScript {
scriptTag
}
... on SrcScript {
src
}
}
}
}
`);
2 changes: 2 additions & 0 deletions core/components/scripts/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ScriptManagerScripts } from './scripts';
export { ScriptsFragment } from './fragment';
Loading
Loading