Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ yarn add @fleetbase/storefront
bun add @fleetbase/storefront
```

It is built on the core [`@fleetbase/sdk`](https://github.com/fleetbase/fleetbase-js) 2, which installs with it. Applications that use both should keep them on the same major version so they share one copy.

The package supports Node.js 22 and 24, native ECMAScript modules (ESM), CommonJS, browsers, server-side rendering, and TypeScript `node16`, `nodenext`, and `bundler` resolution. See the [compatibility policy](docs/compatibility.md) for the tested matrix.

## Quick start
Expand Down Expand Up @@ -168,6 +170,47 @@ const orders = await customer.getOrderHistory({ limit: 20 });

SMS, Apple, Facebook, and Google login helpers remain available. Authorized requests attach `Customer-Token` per request instead of mutating shared adapter headers.

## Realtime order updates

Realtime channels require a short-lived socket token. A signed-in customer can mint one with `customer.socketToken()` (`POST customers/socket-token`, sent with the customer's `Customer-Token`). It resolves to `{ token, expires_in, expires_at }`; the token only authorizes that customer's own channels, such as `order.{order id}` for their orders.

```js
import { create } from 'socketcluster-client';

const customer = await storefront.customers.login('person@example.com', 'password');

// Keep the token in memory only; SocketCluster calls loadToken() before every (re)connect.
let current = null;
const remember = ({ token, expires_in }) => {
current = { token, refreshAt: Date.now() + (expires_in - 60) * 1000 };
return token;
};
const authEngine = {
saveToken: async (_name, token) => token,
removeToken: async () => {
const token = current?.token ?? null;
current = null;
return token;
},
loadToken: async () => (current && Date.now() < current.refreshAt ? current.token : remember(await customer.socketToken())),
};

const socket = create({ hostname: 'socket.example.com', secure: true, port: 443, authEngine });

// Refresh about 60 seconds before the token expires.
setInterval(async () => {
if (current && Date.now() >= current.refreshAt) {
await socket.authenticate(remember(await customer.socketToken()));
}
}, 15_000);

for await (const event of socket.subscribe(`order.${order.id}`)) {
console.log(event.event, event.data);
}
```

`checkout.initialize()` responses also carry a `socket_token` scoped to that checkout's `checkout.{checkout id}` channel. A server without realtime authentication configured answers `customers/socket-token` with `404`; in that case connect without a token as before.

## Adapter replacement

```js
Expand Down
2 changes: 1 addition & 1 deletion dist/@storefront.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/@storefront.min.js.map

Large diffs are not rendered by default.

191 changes: 96 additions & 95 deletions dist/cjs/storefront.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/cjs/storefront.cjs.map

Large diffs are not rendered by default.

189 changes: 94 additions & 95 deletions dist/esm/storefront.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/esm/storefront.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/api-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The executable wire assertions are in `tests/actions.test.js`; the frozen JavaSc

## Customer routes

The static customer-store actions cover creation, email/password login, SMS login, verification codes, and Apple/Facebook/Google login. A hydrated `Customer` supplies request-scoped `Customer-Token` authentication for device registration, places, orders, Stripe helpers, account closure, phone verification, and the compatibility `contacts/{id}` update alias.
The static customer-store actions cover creation, email/password login, SMS login, verification codes, and Apple/Facebook/Google login. A hydrated `Customer` supplies request-scoped `Customer-Token` authentication for device registration, places, orders, realtime socket tokens (`POST customers/socket-token` via `customer.socketToken()`), Stripe helpers, account closure, phone verification, and the compatibility `contacts/{id}` update alias.

The SDK deliberately passes the token in each request's options instead of mutating adapter-global headers. This preserves the server contract while preventing one customer session from leaking into another request.

Expand All @@ -59,5 +59,5 @@ The SDK deliberately passes the token in each request's options instead of mutat
- `src/cart-store` and `src/customer-store` were unreachable duplicate implementations. The historical package had an `exports` map containing only `"."`, so these paths were not supported deep imports. They were removed after their active action-based replacements reached parity.
- `Product.getReviews()` continues sending the product identifier in the legacy `store` parameter. The server route accepts the request and changing it silently could break consumers; any corrected product-review query should be introduced as a separately reviewed API.
- Customer-authorized requests retain the existing public method and header name but now use request-local options rather than adapter-global mutation.
- The package-local `@fleetbase/sdk` declaration shim exists because upstream `@fleetbase/sdk@1.2.13` ships no TypeScript declarations. The shim is strict, contains no public `any`, and is verified by Node16, NodeNext, and Bundler resolution fixtures.
- ESM and CommonJS temporarily bundle the build-time-only `@fleetbase/sdk`. Its current ESM target is reported as having no exports by webpack, while its `.js` CommonJS target sits inside a `type: module` package and fails clean CommonJS consumers when externalized. The vulnerable upstream dependency graph is therefore not installed in consuming applications; patched build-time overrides are compiled into the verified artifacts. Vite, webpack, Next.js, and clean CommonJS fixtures prevent removing this boundary before upstream publishes standards-compliant entries. `countries-list` and `date-fns` remain externalized from the module builds.
- The core SDK, `@fleetbase/sdk` 2, is a regular dependency. It ships its own declarations, ESM and CommonJS entries, and a Fetch transport with no dependencies, so the ESM and CommonJS builds import it instead of bundling a copy. An application that also uses the core SDK shares that copy, so resources, adapters, and `instanceof` checks are the same objects on both sides. Only the self-contained browser bundle inlines it. `countries-list` and `date-fns` stay external as before.
- Listing endpoints (`findAll`, `query`) keep resolving to collections in Storefront's types, although the core SDK types them as a single record or a collection.
6 changes: 1 addition & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"prepack": "pnpm run build"
},
"dependencies": {
"@fleetbase/sdk": "^2.0.0",
"countries-list": "^3.4.1",
"date-fns": "^4.4.0"
},
Expand All @@ -103,7 +104,6 @@
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-terser": "^1.0.0",
"@rollup/plugin-typescript": "^12.3.0",
"@fleetbase/sdk": "1.2.13",
"@stryker-mutator/core": "10.0.0",
"@stryker-mutator/vitest-runner": "10.0.0",
"@vitest/coverage-v8": "^4.0.0",
Expand All @@ -125,10 +125,6 @@
},
"pnpm": {
"overrides": {
"@babel/runtime": "7.28.4",
"axios": "1.20.0",
"follow-redirects": "1.16.0",
"form-data": "4.0.6",
"qs": "6.16.0"
}
}
Expand Down
Loading
Loading