# @sonordev/commerce-kit Changelog

## 1.0.1

- **`server-only` is a dependency.** `@sonordev/commerce-kit/commerce/server` and `@sonordev/commerce-kit/memberships/server` import the `server-only` marker, and 1.0.0 didn't list it. A Next build never noticed, because Next supplies its own copy, but a Node script or a test that imported either entry from a pnpm install failed with `ERR_MODULE_NOT_FOUND`. It's a dependency now, so the import resolves anywhere, and inside a build Next still uses its own copy. Outside Next, load these entries under the `react-server` condition (`node --conditions=react-server`), the way a server component loads them; without it, `server-only` stops the import on purpose. It's the package's only dependency: React, Next and site-kit stay peers of your site.
- **Moving from site-kit 7? Use sonor-setup 7.3.1 or later.** Its codemod writes valid YAML when it adds this package to `pnpm-workspace.yaml`, and it adds the package only to a project whose code imports commerce.

## 1.0.0

The first release. commerce-kit is the commerce module that shipped inside site-kit through 7.4, now its own package, plus memberships. It needs site-kit 8 or newer. Moving a site is an import swap: see [Moving from site-kit 7](https://sonor.dev/commerce-kit#moving-from-site-kit-7).

### Commerce

Products, services, events and checkout, working the way they did in site-kit 7.4. See [Commerce](https://sonor.dev/commerce-kit/commerce).

- **Pages and widgets.** Product pages and grids, offering cards and lists, event tiles, calendars, agendas and an events widget with booking built in, and a size chart for clothing.
- **Checkout.** Inline checkout on the product page, an event checkout with ticket tiers paid by card in the page with Square, registration for free events, shipping rates and address checks when the project ships, and discount codes.
- **Server helpers.** `@sonordev/commerce-kit/commerce/server` fetches offerings, events, static paths and metadata on the server. Its `*Result` variants tell an empty answer from a failed request, so a page can avoid saying "nothing available" when Sonor was only briefly unreachable.
- **Helpers that run anywhere.** `@sonordev/commerce-kit/commerce/utils` has the formatting helpers and the calendar ones (`icsForEvent`, `googleCalendarUrl` and `icsResponse`) with no React, so a server component or a route handler can call them. Through `@sonordev/commerce-kit/commerce`, a client module, they can't be called on the server.
- **Styling hooks.** The components keep their `site-kit-` class names, and event and offering surfaces carry `data-category` and `data-offering-type`, so the stylesheets sites already have keep working.
- **Types.** The root, `@sonordev/commerce-kit`, exports the main commerce types and nothing that runs.

### Memberships

New with this release. Sell a membership on your site and keep members-only pages for the people who pay. See [Memberships](https://sonor.dev/commerce-kit/memberships).

- **Sell it.** `MembershipCheckoutButton` sends a visitor to Square's secure checkout for a membership offering. Square bills the card on the plan's schedule (weekly to yearly, with an optional free trial), and the member shows up in Sonor with their payments. `getMembershipPlans()` lists what the site sells.
- **Members sign in with an emailed link.** `MemberSignInForm` asks for the address they joined with; there are no passwords. Sonor only sends links to the site's own domains, and the form answers the same way for members and strangers.
- **Members-only pages.** `createMembers()` gives you `requireMember()` for a page, `getMember()` for headers and account pages, and the sign-in and sign-out routes. Access is checked with Sonor, so when a membership ends or a renewal fails, access stops within a minute, and the site keeps no secret of its own.
- **Members can cancel on the site.** `members.cancelMembership()` ends a membership at the close of the period that's already paid for.
- **Redirects stay on your site.** The sign-in and sign-out routes answer with paths, never full URLs, so a proxy can't send a member to the wrong host, and the one-time sign-in token never lands in the address bar (a landing page with no query of its own gets `?signed_in=1`).
- **Keep your own sign-in if you have one.** A site with its own accounts asks `getMembershipByEmail(verifiedEmail)` on the server whether someone has paid, and cancels with `cancelMembershipByEmail`.

### What's different from site-kit 7

Apart from the fixes below, the code behaves the same. These are the differences:

- **The package and its paths.** `@sonordev/site-kit/commerce` is now `@sonordev/commerce-kit/commerce`, and `/commerce/server` moved the same way. The commerce types the site-kit root used to re-export come from `@sonordev/commerce-kit`. Nothing is left behind at the old paths; the sonor-setup codemod rewrites the imports.
- **Server logs.** Fetch errors from `commerce/server` start with `[commerce-kit]` instead of `[site-kit/commerce]`.
- **Shopping events reach Sonor.** The product and checkout components report `product_view`, `add_to_cart`, `begin_checkout`, `checkout_start` and `purchase`. site-kit 7 reported them through a hook that only answers inside the analytics provider, and under `SiteKitLayout` a page never is, so they were never sent. They go through site-kit's `trackEvent` now, which waits for analytics to load. See [Analytics](https://sonor.dev/commerce-kit/commerce#analytics).
- **The server helpers return whole offerings.** `commerce/server` re-mapped every offering through a copy of its shape that had fallen behind the API: it read the gallery from a field the API no longer sends, so a server-fetched product had no gallery, and it dropped the clothing flag, size chart, buy link, notes, options and an event's next date. It returns the offering exactly as the browser components load it now, so a page can hand `ProductPage` the product it fetched and the product is in the HTML.
- **Server requests are cached, tagged and time-limited.** They go through site-kit's server data plane: each response is cached for 60 seconds under the `commerce` cache tag, which Sonor expires when you change an offering, so an edit shows within seconds on a site that serves the revalidation route. Each request gives up after 30 seconds and retries a busy API, where before one hung request could stall a build for minutes. Pass `revalidate` in the config to change the window (`0` turns caching off). `getUpcomingEvents` makes one request now; it no longer falls back to a second, older one.
- **Server config is optional.** The helpers read `SONOR_API_KEY` and `SONOR_API_URL` themselves, so `{}` is a complete config, and `createServerConfig` no longer asks for a project id (Sonor finds the project from the key). Existing calls keep working.
- **A sold-out offering keeps its page.** One marked sold out in Sonor came back as "no such offering", so its page returned a 404 and could drop out of search while it was only sold out. It comes back with `status: 'sold_out'` now, and `ProductDetail` and `EventTile` show it as unavailable with buying turned off. If your own markup renders an offering, check `status` too. Drafts and archived offerings still have no page.
- **Checkout works when the browser blocks site storage.** Reading the analytics session id threw in a browser set to block site data, which stopped checkout and event registration before they reached Sonor. The id now comes from site-kit's `readAnalyticsIdentity`, which never throws.
- **Agent tools.** The opt-in `list_offerings` tool in `sonorMcpServer` takes its reader as an option: `offerings: { path: '/shop', read: getOfferingsResult }`, with `getOfferingsResult` from `@sonordev/commerce-kit/commerce/server`. It tells an agent the catalog is unavailable when a request fails; the plain `getOfferings` works too, but it reads an outage as an empty catalog. `list_services` is unchanged.
- **One reader for the Sonor address.** Browser calls get the API address and the site credential from site-kit's `@sonordev/site-kit/runtime`, the same reader the rest of site-kit uses. During server rendering they follow `SONOR_API_URL`, so a staging site's server-rendered requests point at staging.

### Why it's a separate package

Checkout is the busiest money path on a site, and most sites don't sell anything. As its own package, commerce versions and releases on its own: a checkout fix doesn't wait for a site-kit release, and a site-kit release doesn't touch checkout. site-kit also gets a smaller surface: its package is about 8% smaller to download than 7.4.0.

It isn't about page weight. site-kit's root entry was already types only, and nothing in its core referenced commerce, so a visitor to a site without commerce never downloaded any of it. A page that does use commerce loads the same components as before.

commerce-kit takes its transport, credential, analytics, visitor ids and server config from site-kit instead of carrying copies. It imports three site-kit entries, `@sonordev/site-kit/runtime`, `@sonordev/site-kit/analytics` and `@sonordev/site-kit/server`, and nothing else.

### Requirements

- `@sonordev/site-kit` 8.0.0 or newer, installed and mounted with `SiteKitLayout`. It's a peer dependency, so commerce-kit uses your site's one copy.
- React 19 and `react-dom` 19.
- Node 20.19 or newer. The package is ESM only.
- Next 16 is an optional peer. Only `@sonordev/commerce-kit/memberships/server` uses it.
- `SONOR_API_KEY`, the one variable every Sonor site already has. commerce-kit adds none.
