@doswiftly/storefront-sdk 11.2.0 → 11.3.1
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/CHANGELOG.md +98 -0
- package/README.md +214 -2
- package/dist/core/auth/handlers.d.ts.map +1 -1
- package/dist/core/auth/handlers.js +29 -1
- package/dist/core/bot-protection/turnstile-manager.d.ts +0 -1
- package/dist/core/bot-protection/turnstile-manager.d.ts.map +1 -1
- package/dist/core/bot-protection/turnstile-manager.js +0 -1
- package/dist/react/components/AddToCartButton.d.ts +49 -0
- package/dist/react/components/AddToCartButton.d.ts.map +1 -0
- package/dist/react/components/AddToCartButton.js +47 -0
- package/dist/react/components/CartCount.d.ts +35 -0
- package/dist/react/components/CartCount.d.ts.map +1 -0
- package/dist/react/components/CartCount.js +23 -0
- package/dist/react/components/CartTotals.d.ts +54 -0
- package/dist/react/components/CartTotals.d.ts.map +1 -0
- package/dist/react/components/CartTotals.js +38 -0
- package/dist/react/components/Image.d.ts +42 -0
- package/dist/react/components/Image.d.ts.map +1 -0
- package/dist/react/components/Image.js +33 -0
- package/dist/react/components/Money.d.ts +32 -0
- package/dist/react/components/Money.d.ts.map +1 -0
- package/dist/react/components/Money.js +27 -0
- package/dist/react/components/PriceDisplay.d.ts +34 -0
- package/dist/react/components/PriceDisplay.d.ts.map +1 -0
- package/dist/react/components/PriceDisplay.js +21 -0
- package/dist/react/components/index.d.ts +15 -0
- package/dist/react/components/index.d.ts.map +1 -0
- package/dist/react/components/index.js +14 -0
- package/dist/react/hooks/use-auth.d.ts +19 -46
- package/dist/react/hooks/use-auth.d.ts.map +1 -1
- package/dist/react/hooks/use-auth.js +24 -141
- package/dist/react/hooks/use-cart-manager.d.ts +34 -0
- package/dist/react/hooks/use-cart-manager.d.ts.map +1 -1
- package/dist/react/hooks/use-cart-manager.js +22 -19
- package/dist/react/hooks/use-login.d.ts +40 -0
- package/dist/react/hooks/use-login.d.ts.map +1 -0
- package/dist/react/hooks/use-login.js +75 -0
- package/dist/react/hooks/use-logout.d.ts +40 -0
- package/dist/react/hooks/use-logout.d.ts.map +1 -0
- package/dist/react/hooks/use-logout.js +50 -0
- package/dist/react/hooks/use-refresh-token.d.ts +40 -0
- package/dist/react/hooks/use-refresh-token.d.ts.map +1 -0
- package/dist/react/hooks/use-refresh-token.js +66 -0
- package/dist/react/index.d.ts +5 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -0
- package/dist/react/server/get-storefront-client.d.ts +15 -5
- package/dist/react/server/get-storefront-client.d.ts.map +1 -1
- package/dist/react/stores/cart.store.d.ts.map +1 -1
- package/dist/react/stores/cart.store.js +0 -7
- package/dist/react/stores/store-context.d.ts.map +1 -1
- package/dist/react/stores/store-context.js +0 -2
- package/package.json +5 -1
- package/dist/__tests__/unit/test-helpers.d.ts +0 -46
- package/dist/__tests__/unit/test-helpers.d.ts.map +0 -1
- package/dist/__tests__/unit/test-helpers.js +0 -72
- package/dist/__tests__/unit/use-cart-manager.test.d.ts +0 -2
- package/dist/__tests__/unit/use-cart-manager.test.d.ts.map +0 -1
- package/dist/__tests__/unit/use-cart-manager.test.js +0 -400
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,103 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 11.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9ca724e: Removes the `./schema` subpath export from `@doswiftly/storefront-sdk` — it was a duplicate of what `@doswiftly/storefront-operations` already publishes.
|
|
8
|
+
|
|
9
|
+
**Why:**
|
|
10
|
+
|
|
11
|
+
`@doswiftly/storefront-operations` is a linked package — it is always installed alongside `@doswiftly/storefront-sdk`. It exposes the GraphQL SDL as a raw `schema.graphql` file via the `./schema.graphql` subpath export, which is the format every codegen tool and IDE GraphQL plugin natively consumes. Bundling the same SDL again as an ESM string literal in `@doswiftly/storefront-sdk/schema` added ~128 KB to the npm tarball without giving consumers a meaningful capability that wasn't already there.
|
|
12
|
+
|
|
13
|
+
**Migration:**
|
|
14
|
+
|
|
15
|
+
Switch from:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { schema } from "@doswiftly/storefront-sdk/schema";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
to a direct path reference in your codegen config (or `graphql-config` for your IDE):
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// codegen.ts
|
|
25
|
+
const config = {
|
|
26
|
+
schema: "node_modules/@doswiftly/storefront-operations/schema.graphql",
|
|
27
|
+
// …
|
|
28
|
+
};
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The schema content is byte-for-byte identical — your codegen output does not change.
|
|
32
|
+
|
|
33
|
+
**Internal:**
|
|
34
|
+
- Removed `scripts/build-schema-export.cjs` and the matching `pnpm build` chain step.
|
|
35
|
+
- `pnpm build` is now plain `tsc` again.
|
|
36
|
+
- Removed the `dist/schema.js` + `dist/schema.d.ts` build artifacts and the `schema-export.test.ts` unit test.
|
|
37
|
+
- `pnpm run doctor` no longer checks for the schema bundle.
|
|
38
|
+
|
|
39
|
+
`@doswiftly/storefront-operations` is bumped in lockstep (no code change — required because the packages are linked and ship together).
|
|
40
|
+
|
|
41
|
+
## 11.3.0
|
|
42
|
+
|
|
43
|
+
### Minor Changes
|
|
44
|
+
|
|
45
|
+
- 0a2dfd8: Phase 1 of the senior-level audit ships security hardening, dead-code cleanup, and a coverage gate.
|
|
46
|
+
|
|
47
|
+
**Security:**
|
|
48
|
+
- `createSetTokenHandler`, `createClearTokenHandler`, `createWhoamiHandler` now validate the `Origin` header against the `Host` header by parsed URL host (`new URL(origin).host === host`) instead of a substring `includes()` check. The previous implementation was bypassable by empty Host (`includes('')` is always true) and by Origin URLs that embedded the trusted host in their query string. Requests without both `Origin` and `Host` headers now respond with `403`.
|
|
49
|
+
|
|
50
|
+
**Removed:**
|
|
51
|
+
- `ServerClientOptions.getHeaders` (accepted by `getStorefrontClient()` but never wired through). Inject request-scoped headers via the `middleware` option — see updated JSDoc.
|
|
52
|
+
|
|
53
|
+
**Internal:**
|
|
54
|
+
- Inconsistent `setIsRenewingToken` setter in `useAuth` is renamed to `setIsRefreshingToken` to match the exposed `isRefreshingToken` flag.
|
|
55
|
+
- Removed an unused `callbackName` field in `TurnstileManager` and a pre-cookie-era `localStorage.removeItem('cart-storage')` call in `createCartStore`.
|
|
56
|
+
|
|
57
|
+
**Infrastructure:**
|
|
58
|
+
- `package.json` now declares `sideEffects: false` so consumer bundlers can tree-shake unused exports aggressively.
|
|
59
|
+
- Adds `test:coverage` script (`vitest run --coverage`) with a baseline threshold (60% statements / 50% branches / 55% functions / 60% lines). The floor will rise as later phases cover the React providers, auth/currency/language stores, and remaining hooks.
|
|
60
|
+
- Adds 102 new unit tests across `format`, `image`, `cookies`, `auth/handlers`, and additional `errorMiddleware`/`timeoutMiddleware`/`languageMiddleware` cases — including two regression tests for the origin-validation bypass vectors.
|
|
61
|
+
|
|
62
|
+
`@doswiftly/storefront-operations` is bumped in lockstep (no code change — required because the packages are linked and ship together).
|
|
63
|
+
|
|
64
|
+
- 09c3744: Phase 2 of the SDK audit ships the developer-experience features external storefront builders have been writing by hand: pre-built React components, tagged-union mutation state, a bundled GraphQL schema export, and a focused-hook auth API.
|
|
65
|
+
|
|
66
|
+
**Pre-built React components** (from `@doswiftly/storefront-sdk/react`):
|
|
67
|
+
|
|
68
|
+
Six headless, accessibility-aware components — zero styling, framework-agnostic, no `next/image` dependency so they work in any React 18/19 environment.
|
|
69
|
+
- `<Money amount currency>` — locale-formatted price from minor units (wraps `formatPrice`).
|
|
70
|
+
- `<Image data sizes priority>` — `<img>` with thumbhash blur placeholder, lazy by default, `fetchpriority` hint.
|
|
71
|
+
- `<CartCount count label hideWhenEmpty>` — aria-live cart item count.
|
|
72
|
+
- `<AddToCartButton variantId quantity onSuccess onError>` — orchestrates `useCartManager.addItem` with loading state + sr-only error alert.
|
|
73
|
+
- `<PriceDisplay price compareAtPrice currency>` — current price + optional strikethrough sale price.
|
|
74
|
+
- `<CartTotals subtotal discount shipping tax total currency labels>` — `<dl>` breakdown that elides empty rows.
|
|
75
|
+
|
|
76
|
+
**Tagged-union status in `useCartManager`**:
|
|
77
|
+
- New `status` field on the hook return — `{ type: 'idle' | 'loading' | 'error' | 'success', operation? }` — enables exhaustive switching without juggling booleans.
|
|
78
|
+
- `isLoading` and `error` remain available as derived selectors (backward-compatible).
|
|
79
|
+
- `clearCart()` resets status to `idle`.
|
|
80
|
+
|
|
81
|
+
**Bundled GraphQL schema** (`@doswiftly/storefront-sdk/schema`):
|
|
82
|
+
- New export path ships the full GraphQL SDL (123 KB) as a string constant, regenerated from `@doswiftly/storefront-operations/schema.graphql` on each build.
|
|
83
|
+
- Wire GraphQL codegen / IDE plugins without a live backend — no extra `npm install`.
|
|
84
|
+
|
|
85
|
+
**`useAuth` split into focused hooks**:
|
|
86
|
+
- `useLogin({ onSetToken })` — login flow only, `{ login, isLoggingIn, error }`.
|
|
87
|
+
- `useLogout({ onClearToken })` — logout, always clears local store even on backend failure.
|
|
88
|
+
- `useRefreshToken({ onSetToken })` — token rotation, keeps existing customer.
|
|
89
|
+
- `useAuth(options)` is preserved as a thin facade composing the three (backward-compatible, no breaking change).
|
|
90
|
+
|
|
91
|
+
Prefer the focused hooks in new code — smaller bundles, isolated state, smaller dependency arrays.
|
|
92
|
+
|
|
93
|
+
**Docs**:
|
|
94
|
+
- README adds a 7-step Next.js 16 App Router setup, BFF route handler examples, components catalog, bundled schema usage.
|
|
95
|
+
- New `docs/storefront-developer/sdk/nextjs-setup.md` and `auth.md` with end-to-end recipes.
|
|
96
|
+
|
|
97
|
+
**Tests**: 476 → 494 unit tests (+18). New files: `components.test.tsx` (37 cases), `schema-export.test.ts` (5 cases), `use-auth.test.tsx` (13 cases). Build clean, 0-deps invariant preserved.
|
|
98
|
+
|
|
99
|
+
`@doswiftly/storefront-operations` is bumped in lockstep (no code change — required because the packages are linked and ship together).
|
|
100
|
+
|
|
3
101
|
## 11.2.0
|
|
4
102
|
|
|
5
103
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @doswiftly/storefront-sdk
|
|
2
2
|
|
|
3
|
-
Layered runtime SDK for DoSwiftly Commerce storefronts.
|
|
3
|
+
Layered runtime SDK for DoSwiftly Commerce storefronts. Framework-agnostic core + React adapter, **0 runtime dependencies** in core.
|
|
4
4
|
|
|
5
5
|
## Architecture
|
|
6
6
|
|
|
@@ -94,10 +94,222 @@ const { currency, setCurrency } = useCurrencyStore();
|
|
|
94
94
|
| Path | Description | Dependencies |
|
|
95
95
|
|------|-------------|-------------|
|
|
96
96
|
| `@doswiftly/storefront-sdk` | Core: transport, middleware, clients, errors, format, image types, sanitize, auth handlers, route matching | **0** |
|
|
97
|
-
| `@doswiftly/storefront-sdk/react` | Providers, hooks,
|
|
97
|
+
| `@doswiftly/storefront-sdk/react` | Providers, hooks, pre-built UI components, Zustand stores | react, zustand |
|
|
98
98
|
| `@doswiftly/storefront-sdk/react/server` | Server-side client factory | react |
|
|
99
99
|
| `@doswiftly/storefront-sdk/cache` | Cache strategy functions | **0** |
|
|
100
100
|
|
|
101
|
+
## Next.js 16 App Router — first add-to-cart in 15 minutes
|
|
102
|
+
|
|
103
|
+
End-to-end skeleton that gets you a working cart + auth flow without writing
|
|
104
|
+
the middleware pipeline by hand. Copy the files below into a fresh `app/`
|
|
105
|
+
directory and you're live.
|
|
106
|
+
|
|
107
|
+
**1. `app/layout.tsx`** — wrap the tree in `StorefrontProvider`:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { StorefrontProvider } from '@doswiftly/storefront-sdk/react';
|
|
111
|
+
import { getStorefrontClient } from '@doswiftly/storefront-sdk/react/server';
|
|
112
|
+
import { cookies } from 'next/headers';
|
|
113
|
+
import { AUTH_COOKIE_NAME } from '@doswiftly/storefront-sdk';
|
|
114
|
+
|
|
115
|
+
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
|
116
|
+
const serverClient = getStorefrontClient({
|
|
117
|
+
apiUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
118
|
+
shopSlug: process.env.NEXT_PUBLIC_SHOP_SLUG!,
|
|
119
|
+
});
|
|
120
|
+
// `SHOP_BASICS_QUERY` is your own operation — generated by graphql-codegen
|
|
121
|
+
// from `@doswiftly/storefront-operations/schema.graphql`. It should request
|
|
122
|
+
// `shop.currency`, `shop.supportedCurrencies`, `shop.supportedLanguages`,
|
|
123
|
+
// `shop.botProtection` — whatever StorefrontProvider needs.
|
|
124
|
+
const { shop } = await serverClient.query(SHOP_BASICS_QUERY);
|
|
125
|
+
|
|
126
|
+
// Read the httpOnly auth cookie server-side so we can hydrate the auth store
|
|
127
|
+
// without a flash of "Sign In".
|
|
128
|
+
const cookieStore = await cookies();
|
|
129
|
+
const initialIsAuthenticated = Boolean(cookieStore.get(AUTH_COOKIE_NAME)?.value);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<html lang="pl">
|
|
133
|
+
<body>
|
|
134
|
+
<StorefrontProvider
|
|
135
|
+
config={{
|
|
136
|
+
apiUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
137
|
+
shopSlug: process.env.NEXT_PUBLIC_SHOP_SLUG!,
|
|
138
|
+
}}
|
|
139
|
+
shopData={shop}
|
|
140
|
+
initialIsAuthenticated={initialIsAuthenticated}
|
|
141
|
+
>
|
|
142
|
+
{children}
|
|
143
|
+
</StorefrontProvider>
|
|
144
|
+
</body>
|
|
145
|
+
</html>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**2. `app/api/auth/set-token/route.ts`** — BFF route that sets the httpOnly
|
|
151
|
+
cookie. The SDK ships factories so each file is two lines:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { createSetTokenHandler } from '@doswiftly/storefront-sdk';
|
|
155
|
+
export const POST = createSetTokenHandler();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**3. `app/api/auth/clear-token/route.ts`** — clears the cookie on logout:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { createClearTokenHandler } from '@doswiftly/storefront-sdk';
|
|
162
|
+
export const POST = createClearTokenHandler();
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**4. `app/api/auth/whoami/route.ts`** — hydrates customer state from the
|
|
166
|
+
httpOnly cookie after a hard refresh (`accessToken` no longer persists in
|
|
167
|
+
localStorage — XSS hardening):
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { createWhoamiHandler } from '@doswiftly/storefront-sdk';
|
|
171
|
+
|
|
172
|
+
export const GET = createWhoamiHandler({
|
|
173
|
+
apiUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
174
|
+
shopSlug: process.env.NEXT_PUBLIC_SHOP_SLUG!,
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**5. Sign-in form** — uses the focused `useLogin` hook + wires the BFF
|
|
179
|
+
callback:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
'use client';
|
|
183
|
+
import { useLogin } from '@doswiftly/storefront-sdk/react';
|
|
184
|
+
|
|
185
|
+
export function LoginForm() {
|
|
186
|
+
const { login, isLoggingIn, error } = useLogin({
|
|
187
|
+
onSetToken: async (token) => {
|
|
188
|
+
await fetch('/api/auth/set-token', {
|
|
189
|
+
method: 'POST',
|
|
190
|
+
headers: { 'Content-Type': 'application/json' },
|
|
191
|
+
body: JSON.stringify({ token }),
|
|
192
|
+
});
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<form onSubmit={async (e) => {
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
const data = new FormData(e.currentTarget);
|
|
200
|
+
const result = await login(String(data.get('email')), String(data.get('password')));
|
|
201
|
+
if (result.success) location.href = '/account';
|
|
202
|
+
}}>
|
|
203
|
+
<input name="email" type="email" required />
|
|
204
|
+
<input name="password" type="password" required />
|
|
205
|
+
<button type="submit" disabled={isLoggingIn}>{isLoggingIn ? 'Signing in…' : 'Sign in'}</button>
|
|
206
|
+
{error && <p role="alert">{error}</p>}
|
|
207
|
+
</form>
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**6. Product card with add-to-cart** — pre-built components:
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
'use client';
|
|
216
|
+
import { Image, PriceDisplay, AddToCartButton } from '@doswiftly/storefront-sdk/react';
|
|
217
|
+
|
|
218
|
+
export function ProductCard({ product }: { product: ProductCardFields }) {
|
|
219
|
+
return (
|
|
220
|
+
<article>
|
|
221
|
+
<Image data={product.featuredImage} sizes="(max-width: 768px) 100vw, 33vw" />
|
|
222
|
+
<h2>{product.title}</h2>
|
|
223
|
+
<PriceDisplay
|
|
224
|
+
price={product.price.amount * 100}
|
|
225
|
+
compareAtPrice={product.compareAtPrice ? product.compareAtPrice.amount * 100 : undefined}
|
|
226
|
+
currency={product.price.currencyCode}
|
|
227
|
+
/>
|
|
228
|
+
<AddToCartButton variantId={product.firstVariant.id}>Add to cart</AddToCartButton>
|
|
229
|
+
</article>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
That's it. Cart recovery (stale-cart auto-replay) is automatic — wire a single
|
|
235
|
+
global `onExpired` toast subscriber once in `app/layout.tsx`:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
'use client';
|
|
239
|
+
import { useEffect } from 'react';
|
|
240
|
+
import { useCartManager } from '@doswiftly/storefront-sdk/react';
|
|
241
|
+
import { toast } from 'sonner';
|
|
242
|
+
|
|
243
|
+
export function CartExpiredToast() {
|
|
244
|
+
const { onExpired } = useCartManager();
|
|
245
|
+
useEffect(() => onExpired((e) => {
|
|
246
|
+
toast.error(e.reason === 'state-dependent'
|
|
247
|
+
? 'Twój koszyk wygasł, dodaj produkty ponownie'
|
|
248
|
+
: 'Nie udało się odzyskać koszyka, spróbuj ponownie');
|
|
249
|
+
}), [onExpired]);
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Pre-built React components
|
|
255
|
+
|
|
256
|
+
Headless, accessibility-aware, zero styling — pass `className` to integrate
|
|
257
|
+
with your CSS approach. Available from `@doswiftly/storefront-sdk/react`:
|
|
258
|
+
|
|
259
|
+
| Component | Purpose |
|
|
260
|
+
|-----------|---------|
|
|
261
|
+
| `<Money amount currency>` | Locale-formatted price string from minor units |
|
|
262
|
+
| `<Image data sizes priority>` | `<img>` with thumbhash blur placeholder + sane defaults |
|
|
263
|
+
| `<CartCount count label>` | Aria-live cart item count |
|
|
264
|
+
| `<AddToCartButton variantId quantity>` | Button wired to `useCartManager.addItem` |
|
|
265
|
+
| `<PriceDisplay price compareAtPrice currency>` | Price + optional strikethrough sale price |
|
|
266
|
+
| `<CartTotals subtotal tax shipping discount total currency>` | Cart financial breakdown `<dl>` |
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { Money, Image, CartCount, AddToCartButton, PriceDisplay, CartTotals } from '@doswiftly/storefront-sdk/react';
|
|
270
|
+
|
|
271
|
+
<Money amount={9990} currency="PLN" /> {/* "99,90 zł" */}
|
|
272
|
+
<PriceDisplay price={7990} compareAtPrice={9990} currency="PLN" />
|
|
273
|
+
<CartCount count={3} label="items" />
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## GraphQL schema for codegen
|
|
277
|
+
|
|
278
|
+
The GraphQL SDL ships in the linked `@doswiftly/storefront-operations` package
|
|
279
|
+
(which is installed alongside the SDK) — point your codegen / IDE plugin at it
|
|
280
|
+
directly. No live backend required:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// codegen.ts — uses raw .graphql file from the operations package
|
|
284
|
+
const config = {
|
|
285
|
+
schema: 'node_modules/@doswiftly/storefront-operations/schema.graphql',
|
|
286
|
+
documents: ['./app/**/*.{ts,tsx,graphql}'],
|
|
287
|
+
generates: { './generated/graphql.ts': { /* … */ } },
|
|
288
|
+
};
|
|
289
|
+
export default config;
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
For VS Code GraphQL extension, point `graphql-config` at the same path.
|
|
293
|
+
|
|
294
|
+
## Auth: focused hooks vs the facade
|
|
295
|
+
|
|
296
|
+
Prefer the per-flow hooks in new code — smaller bundles, isolated state:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
import { useLogin, useLogout, useRefreshToken } from '@doswiftly/storefront-sdk/react';
|
|
300
|
+
|
|
301
|
+
const { login, isLoggingIn } = useLogin({ onSetToken });
|
|
302
|
+
const { logout, isLoggingOut } = useLogout({ onClearToken });
|
|
303
|
+
const { refreshToken } = useRefreshToken({ onSetToken });
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The legacy `useAuth` facade still works (combines all three):
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
import { useAuth } from '@doswiftly/storefront-sdk/react';
|
|
310
|
+
const { login, logout, refreshToken, isLoading, error } = useAuth({ onSetToken, onClearToken });
|
|
311
|
+
```
|
|
312
|
+
|
|
101
313
|
## Core API
|
|
102
314
|
|
|
103
315
|
### createStorefrontClient
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/core/auth/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,UAAU,sBAAsB;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../../src/core/auth/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,UAAU,sBAAsB;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,CAAC,EAAE,sBAAsB,GACjC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAgEzC;AAED,UAAU,oBAAoB;IAC5B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,oBAAyB,GACjC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA6DzC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAkCjF"}
|
|
@@ -27,15 +27,43 @@ function serializeCookie(name, value, options) {
|
|
|
27
27
|
parts.push('HttpOnly');
|
|
28
28
|
return parts.join('; ');
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate the Origin header against the Host header to prevent CSRF from
|
|
32
|
+
* unrelated origins.
|
|
33
|
+
*
|
|
34
|
+
* Compares the *parsed* origin's host with the request's host header, not a
|
|
35
|
+
* substring match — `origin.includes(host)` was bypassable two ways:
|
|
36
|
+
* - `host = ""` (or missing) → `origin.includes('')` always true;
|
|
37
|
+
* - `origin = "https://attacker.example/?host=trusted"` → substring match
|
|
38
|
+
* passes for any trusted host name.
|
|
39
|
+
*
|
|
40
|
+
* Both Origin and Host MUST be present (typical browser fetch sends Origin
|
|
41
|
+
* for cross-origin and same-origin POSTs; server-to-server callers without
|
|
42
|
+
* an Origin header should hit the GraphQL API directly, not these BFF
|
|
43
|
+
* handlers).
|
|
44
|
+
*/
|
|
30
45
|
function validateOrigin(request) {
|
|
31
46
|
const origin = request.headers.get('origin');
|
|
32
47
|
const host = request.headers.get('host');
|
|
33
|
-
if (origin
|
|
48
|
+
if (!origin || !host) {
|
|
49
|
+
return new Response(JSON.stringify({ error: 'Origin and Host headers are required' }), { status: 403, headers: { 'Content-Type': 'application/json' } });
|
|
50
|
+
}
|
|
51
|
+
let originHost;
|
|
52
|
+
try {
|
|
53
|
+
originHost = new URL(origin).host;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
34
56
|
return new Response(JSON.stringify({ error: 'Invalid origin' }), {
|
|
35
57
|
status: 403,
|
|
36
58
|
headers: { 'Content-Type': 'application/json' },
|
|
37
59
|
});
|
|
38
60
|
}
|
|
61
|
+
if (originHost !== host) {
|
|
62
|
+
return new Response(JSON.stringify({ error: 'Origin mismatch' }), {
|
|
63
|
+
status: 403,
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
39
67
|
return null;
|
|
40
68
|
}
|
|
41
69
|
/**
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { AbstractBotProtectionManager } from './abstract-manager';
|
|
8
8
|
export declare class TurnstileManager extends AbstractBotProtectionManager {
|
|
9
|
-
private callbackName;
|
|
10
9
|
protected _waitForApi(): Promise<void>;
|
|
11
10
|
protected _renderWidget(container: HTMLElement, action?: string): string;
|
|
12
11
|
protected _executeChallenge(widgetId: string, action?: string): Promise<string | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turnstile-manager.d.ts","sourceRoot":"","sources":["../../../src/core/bot-protection/turnstile-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAElE,qBAAa,gBAAiB,SAAQ,4BAA4B;IAChE,
|
|
1
|
+
{"version":3,"file":"turnstile-manager.d.ts","sourceRoot":"","sources":["../../../src/core/bot-protection/turnstile-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAElE,qBAAa,gBAAiB,SAAQ,4BAA4B;IAChE,SAAS,CAAC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBtC,SAAS,CAAC,aAAa,CAAC,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM;IAaxE,SAAS,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuCtF,SAAS,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAKjD"}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
/// <reference path="./types/turnstile.d.ts" />
|
|
8
8
|
import { AbstractBotProtectionManager } from './abstract-manager';
|
|
9
9
|
export class TurnstileManager extends AbstractBotProtectionManager {
|
|
10
|
-
callbackName = `onloadTurnstileCallback_${Math.random().toString(36).slice(2)}`;
|
|
11
10
|
_waitForApi() {
|
|
12
11
|
if (typeof window !== 'undefined' && window.turnstile) {
|
|
13
12
|
return Promise.resolve();
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<AddToCartButton>` — minimal opinionated "add to cart" trigger that wires
|
|
3
|
+
* `useCartManager().addItem` to a `<button>` with loading state and error
|
|
4
|
+
* surfacing.
|
|
5
|
+
*
|
|
6
|
+
* Headless: no styling. Renders a button that:
|
|
7
|
+
* - disables itself while the mutation is in flight (`aria-busy`),
|
|
8
|
+
* - exposes the last error to assistive tech via a visually-hidden alert,
|
|
9
|
+
* - invokes `onSuccess` / `onError` callbacks for consumer-side toasts
|
|
10
|
+
* or analytics.
|
|
11
|
+
*
|
|
12
|
+
* The component does **not** subscribe to the global `onExpired` event — that
|
|
13
|
+
* is the consumer's responsibility (typically a single global toast). On
|
|
14
|
+
* stale-cart recovery the SDK swaps the cart transparently and this button
|
|
15
|
+
* just reports success.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <AddToCartButton variantId={variant.id} quantity={1}>
|
|
20
|
+
* Add to cart
|
|
21
|
+
* </AddToCartButton>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
import { type ButtonHTMLAttributes, type ReactNode } from 'react';
|
|
25
|
+
interface CartLineAttribute {
|
|
26
|
+
key: string;
|
|
27
|
+
value: string;
|
|
28
|
+
}
|
|
29
|
+
type ButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'type' | 'onClick' | 'aria-busy' | 'onError'>;
|
|
30
|
+
export interface AddToCartButtonProps extends ButtonProps {
|
|
31
|
+
/** Product variant identifier to add. */
|
|
32
|
+
variantId: string;
|
|
33
|
+
/** Quantity (default 1). */
|
|
34
|
+
quantity?: number;
|
|
35
|
+
/** Cart line attributes (custom options, gift wrap, etc.). */
|
|
36
|
+
attributes?: CartLineAttribute[];
|
|
37
|
+
/** Button label / contents. Defaults to "Add to cart". */
|
|
38
|
+
children?: ReactNode;
|
|
39
|
+
/** Called after the mutation succeeds with the updated cart summary. */
|
|
40
|
+
onSuccess?: (result: {
|
|
41
|
+
cartId: string;
|
|
42
|
+
totalQuantity: number;
|
|
43
|
+
}) => void;
|
|
44
|
+
/** Called when the mutation throws (does not bubble through React Error Boundary). */
|
|
45
|
+
onError?: (error: Error) => void;
|
|
46
|
+
}
|
|
47
|
+
export declare function AddToCartButton({ variantId, quantity, attributes, children, onSuccess, onError, disabled, ...buttonProps }: AddToCartButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=AddToCartButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddToCartButton.d.ts","sourceRoot":"","sources":["../../../src/react/components/AddToCartButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAAY,KAAK,oBAAoB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG5E,UAAU,iBAAiB;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,KAAK,WAAW,GAAG,IAAI,CACrB,oBAAoB,CAAC,iBAAiB,CAAC,EAEvC,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAC7C,CAAC;AAEF,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,QAAY,EACZ,UAAU,EACV,QAAQ,EACR,SAAS,EACT,OAAO,EACP,QAAQ,EACR,GAAG,WAAW,EACf,EAAE,oBAAoB,2CAqCtB"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<AddToCartButton>` — minimal opinionated "add to cart" trigger that wires
|
|
3
|
+
* `useCartManager().addItem` to a `<button>` with loading state and error
|
|
4
|
+
* surfacing.
|
|
5
|
+
*
|
|
6
|
+
* Headless: no styling. Renders a button that:
|
|
7
|
+
* - disables itself while the mutation is in flight (`aria-busy`),
|
|
8
|
+
* - exposes the last error to assistive tech via a visually-hidden alert,
|
|
9
|
+
* - invokes `onSuccess` / `onError` callbacks for consumer-side toasts
|
|
10
|
+
* or analytics.
|
|
11
|
+
*
|
|
12
|
+
* The component does **not** subscribe to the global `onExpired` event — that
|
|
13
|
+
* is the consumer's responsibility (typically a single global toast). On
|
|
14
|
+
* stale-cart recovery the SDK swaps the cart transparently and this button
|
|
15
|
+
* just reports success.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <AddToCartButton variantId={variant.id} quantity={1}>
|
|
20
|
+
* Add to cart
|
|
21
|
+
* </AddToCartButton>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
'use client';
|
|
25
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
26
|
+
import { useState } from 'react';
|
|
27
|
+
import { useCartManager } from '../hooks/use-cart-manager';
|
|
28
|
+
export function AddToCartButton({ variantId, quantity = 1, attributes, children, onSuccess, onError, disabled, ...buttonProps }) {
|
|
29
|
+
const { addItem, isLoading } = useCartManager();
|
|
30
|
+
const [localError, setLocalError] = useState(null);
|
|
31
|
+
const handleClick = async () => {
|
|
32
|
+
setLocalError(null);
|
|
33
|
+
try {
|
|
34
|
+
const result = await addItem([{ variantId, quantity, attributes }]);
|
|
35
|
+
onSuccess?.({
|
|
36
|
+
cartId: result.cart.id,
|
|
37
|
+
totalQuantity: result.cart.totalQuantity,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
const error = err instanceof Error ? err : new Error('Failed to add to cart');
|
|
42
|
+
setLocalError(error);
|
|
43
|
+
onError?.(error);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { ...buttonProps, type: "button", onClick: handleClick, disabled: disabled || isLoading, "aria-busy": isLoading || undefined, children: children ?? 'Add to cart' }), localError && (_jsx("span", { role: "alert", style: { position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }, children: localError.message }))] }));
|
|
47
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<CartCount>` — render the cart item count with screen-reader-friendly
|
|
3
|
+
* semantics (`aria-live="polite"`).
|
|
4
|
+
*
|
|
5
|
+
* Receives `count` explicitly rather than reading from a store — the SDK's
|
|
6
|
+
* cart store tracks `cartId`, not line totals (which come from a GraphQL
|
|
7
|
+
* query in the consumer). This keeps the component reusable across data
|
|
8
|
+
* layers (React Query, SWR, Apollo, custom fetcher).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { data } = useCartQuery(cartId);
|
|
13
|
+
* <CartCount count={data?.totalQuantity ?? 0} />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export interface CartCountProps {
|
|
17
|
+
/** Number of items in the cart. */
|
|
18
|
+
count: number;
|
|
19
|
+
/** Class name for styling. */
|
|
20
|
+
className?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Custom formatter (e.g. localised pluralisation).
|
|
23
|
+
* Default: stringifies the count.
|
|
24
|
+
*/
|
|
25
|
+
formatter?: (count: number) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Optional label rendered after the count (e.g. "items"). Use a localised
|
|
28
|
+
* string from the consumer's i18n layer rather than hardcoding.
|
|
29
|
+
*/
|
|
30
|
+
label?: string;
|
|
31
|
+
/** Hide entirely when count is zero (default `false`). */
|
|
32
|
+
hideWhenEmpty?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function CartCount({ count, className, formatter, label, hideWhenEmpty, }: CartCountProps): import("react/jsx-runtime").JSX.Element | null;
|
|
35
|
+
//# sourceMappingURL=CartCount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CartCount.d.ts","sourceRoot":"","sources":["../../../src/react/components/CartCount.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,SAAS,EACT,SAAS,EACT,KAAK,EACL,aAAqB,GACtB,EAAE,cAAc,kDAYhB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<CartCount>` — render the cart item count with screen-reader-friendly
|
|
3
|
+
* semantics (`aria-live="polite"`).
|
|
4
|
+
*
|
|
5
|
+
* Receives `count` explicitly rather than reading from a store — the SDK's
|
|
6
|
+
* cart store tracks `cartId`, not line totals (which come from a GraphQL
|
|
7
|
+
* query in the consumer). This keeps the component reusable across data
|
|
8
|
+
* layers (React Query, SWR, Apollo, custom fetcher).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* const { data } = useCartQuery(cartId);
|
|
13
|
+
* <CartCount count={data?.totalQuantity ?? 0} />
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
'use client';
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
export function CartCount({ count, className, formatter, label, hideWhenEmpty = false, }) {
|
|
19
|
+
if (hideWhenEmpty && count === 0)
|
|
20
|
+
return null;
|
|
21
|
+
const text = formatter ? formatter(count) : String(count);
|
|
22
|
+
return (_jsxs("span", { className: className, "aria-live": "polite", "aria-atomic": "true", children: [_jsx("span", { "aria-hidden": "true", children: text }), _jsxs("span", { className: "sr-only", children: [text, label ? ` ${label}` : ''] })] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<CartTotals>` — render a description list (`<dl>`) of cart financial
|
|
3
|
+
* breakdown rows: subtotal, optional discount / shipping / tax, total.
|
|
4
|
+
*
|
|
5
|
+
* Renders only the rows you pass. `subtotal`, `total`, and `currency` are
|
|
6
|
+
* required; everything else is optional and elided when undefined or zero
|
|
7
|
+
* (discount specifically — pass zero to hide rather than showing "-0,00").
|
|
8
|
+
*
|
|
9
|
+
* Labels are passed by the consumer (i18n is its responsibility). The
|
|
10
|
+
* component defaults to English labels so it works out of the box during
|
|
11
|
+
* prototyping.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <CartTotals
|
|
16
|
+
* subtotal={cart.cost.subtotal.amount * 100}
|
|
17
|
+
* shipping={cart.cost.shipping?.amount * 100}
|
|
18
|
+
* tax={cart.cost.tax?.amount * 100}
|
|
19
|
+
* total={cart.cost.total.amount * 100}
|
|
20
|
+
* currency={cart.cost.total.currencyCode}
|
|
21
|
+
* labels={{ subtotal: 'Suma częściowa', shipping: 'Dostawa', tax: 'Podatek', total: 'Razem' }}
|
|
22
|
+
* />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { ReactNode } from 'react';
|
|
26
|
+
export interface CartTotalsLabels {
|
|
27
|
+
subtotal?: ReactNode;
|
|
28
|
+
discount?: ReactNode;
|
|
29
|
+
shipping?: ReactNode;
|
|
30
|
+
tax?: ReactNode;
|
|
31
|
+
total?: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
export interface CartTotalsProps {
|
|
34
|
+
/** Subtotal in minor units. */
|
|
35
|
+
subtotal: number;
|
|
36
|
+
/** Discount in minor units. Rendered as `−<amount>` when > 0. */
|
|
37
|
+
discount?: number;
|
|
38
|
+
/** Shipping in minor units. Rendered when provided (incl. 0 = free shipping). */
|
|
39
|
+
shipping?: number;
|
|
40
|
+
/** Tax in minor units. Rendered when provided. */
|
|
41
|
+
tax?: number;
|
|
42
|
+
/** Total in minor units. */
|
|
43
|
+
total: number;
|
|
44
|
+
/** ISO 4217 currency code shared by every line. */
|
|
45
|
+
currency: string;
|
|
46
|
+
/** Class applied to the wrapping `<dl>`. */
|
|
47
|
+
className?: string;
|
|
48
|
+
/** Class applied to each `<div>` row (subtotal, discount, etc.). */
|
|
49
|
+
rowClassName?: string;
|
|
50
|
+
/** Custom row labels. Defaults to English. */
|
|
51
|
+
labels?: CartTotalsLabels;
|
|
52
|
+
}
|
|
53
|
+
export declare function CartTotals({ subtotal, discount, shipping, tax, total, currency, className, rowClassName, labels, }: CartTotalsProps): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
//# sourceMappingURL=CartTotals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CartTotals.d.ts","sourceRoot":"","sources":["../../../src/react/components/CartTotals.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAUD,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,MAAM,GACP,EAAE,eAAe,2CAoCjB"}
|