@develemit/storefront 0.0.0-stage → 0.2.0

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.
Files changed (67) hide show
  1. package/README.md +244 -2
  2. package/dist/cart/cart-provider.d.ts +25 -0
  3. package/dist/cart/cart-provider.d.ts.map +1 -0
  4. package/dist/cart/cart-reducer.d.ts +32 -0
  5. package/dist/cart/cart-reducer.d.ts.map +1 -0
  6. package/dist/cart/cart-storage.d.ts +7 -0
  7. package/dist/cart/cart-storage.d.ts.map +1 -0
  8. package/dist/client/commerce-client.d.ts +14 -0
  9. package/dist/client/commerce-client.d.ts.map +1 -0
  10. package/dist/client/errors.d.ts +13 -0
  11. package/dist/client/errors.d.ts.map +1 -0
  12. package/dist/client/index.d.ts +3 -0
  13. package/dist/client/index.d.ts.map +1 -0
  14. package/dist/client.js +62 -0
  15. package/dist/components/add-to-cart-button/add-to-cart-button.d.ts +15 -0
  16. package/dist/components/add-to-cart-button/add-to-cart-button.d.ts.map +1 -0
  17. package/dist/components/availability-badge/availability-badge.d.ts +16 -0
  18. package/dist/components/availability-badge/availability-badge.d.ts.map +1 -0
  19. package/dist/components/cart-button/cart-button.d.ts +10 -0
  20. package/dist/components/cart-button/cart-button.d.ts.map +1 -0
  21. package/dist/components/cart-drawer/cart-drawer.d.ts +19 -0
  22. package/dist/components/cart-drawer/cart-drawer.d.ts.map +1 -0
  23. package/dist/components/cart-drawer/use-drawer-presence.d.ts +6 -0
  24. package/dist/components/cart-drawer/use-drawer-presence.d.ts.map +1 -0
  25. package/dist/components/cart-line-item/cart-line-item.d.ts +12 -0
  26. package/dist/components/cart-line-item/cart-line-item.d.ts.map +1 -0
  27. package/dist/components/cart-summary/cart-summary.d.ts +11 -0
  28. package/dist/components/cart-summary/cart-summary.d.ts.map +1 -0
  29. package/dist/components/category-filter/category-filter.d.ts +12 -0
  30. package/dist/components/category-filter/category-filter.d.ts.map +1 -0
  31. package/dist/components/fixtures.d.ts +3 -0
  32. package/dist/components/fixtures.d.ts.map +1 -0
  33. package/dist/components/price-tag/price-tag.d.ts +8 -0
  34. package/dist/components/price-tag/price-tag.d.ts.map +1 -0
  35. package/dist/components/product-card/product-card.d.ts +10 -0
  36. package/dist/components/product-card/product-card.d.ts.map +1 -0
  37. package/dist/components/product-gallery/product-gallery.d.ts +7 -0
  38. package/dist/components/product-gallery/product-gallery.d.ts.map +1 -0
  39. package/dist/components/product-grid/product-grid.d.ts +11 -0
  40. package/dist/components/product-grid/product-grid.d.ts.map +1 -0
  41. package/dist/components/render-with-cart.d.ts +4 -0
  42. package/dist/components/render-with-cart.d.ts.map +1 -0
  43. package/dist/format/index.d.ts +3 -0
  44. package/dist/format/index.d.ts.map +1 -0
  45. package/dist/format.js +1 -0
  46. package/dist/index.d.ts +23 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +672 -0
  49. package/dist/lib/format-price.d.ts +2 -0
  50. package/dist/lib/format-price.d.ts.map +1 -0
  51. package/dist/lib/parse-duration.d.ts +2 -0
  52. package/dist/lib/parse-duration.d.ts.map +1 -0
  53. package/dist/lib/provider.d.ts +29 -0
  54. package/dist/lib/provider.d.ts.map +1 -0
  55. package/dist/lib/theme.d.ts +16 -0
  56. package/dist/lib/theme.d.ts.map +1 -0
  57. package/dist/sort-for-display-Cj6GMeCY.js +13 -0
  58. package/dist/styles.css +454 -0
  59. package/dist/views/featured-products.d.ts +9 -0
  60. package/dist/views/featured-products.d.ts.map +1 -0
  61. package/dist/views/product-detail-view.d.ts +14 -0
  62. package/dist/views/product-detail-view.d.ts.map +1 -0
  63. package/dist/views/shop-view.d.ts +20 -0
  64. package/dist/views/shop-view.d.ts.map +1 -0
  65. package/dist/views/sort-for-display.d.ts +3 -0
  66. package/dist/views/sort-for-display.d.ts.map +1 -0
  67. package/package.json +46 -3
package/README.md CHANGED
@@ -1,3 +1,245 @@
1
- # Temporary Holding Version
1
+ # @develemit/storefront
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Brand-agnostic storefront UI and commerce logic, built so it can be extracted
4
+ and published as a standalone package.
5
+
6
+ ## Boundary rules
7
+
8
+ - No client branding, copy, colors, or product data in this package.
9
+ The consuming app supplies those through props, theme tokens, and data adapters.
10
+ - No imports from `apps/*` or other app-specific packages.
11
+ - React, React DOM, and Next are peer dependencies, never bundled.
12
+
13
+ ## Commands
14
+
15
+ - `pnpm exec nx build storefront` — library build to `packages/storefront/dist`
16
+ - `pnpm exec nx test storefront`
17
+
18
+ ## Theme contract
19
+
20
+ Import `@develemit/storefront/styles.css` for neutral defaults, then override
21
+ the variables in the host app (or pass a `StorefrontTheme` through
22
+ `themeToCssVars()`).
23
+
24
+ | Variable | Purpose |
25
+ | ----------------------------- | ------------------------------------------------- |
26
+ | `--sf-color-bg` | Page background |
27
+ | `--sf-color-surface` | Cards and raised surfaces |
28
+ | `--sf-color-fg` | Primary text |
29
+ | `--sf-color-muted` | Secondary text |
30
+ | `--sf-color-accent` | Buttons and links |
31
+ | `--sf-color-accent-fg` | Text on accent backgrounds |
32
+ | `--sf-color-border` | Borders and dividers |
33
+ | `--sf-color-sold` | Sold / unavailable treatment |
34
+ | `--sf-radius` | Corner radius |
35
+ | `--sf-font-display` | Headings font stack |
36
+ | `--sf-font-body` | Body font stack |
37
+ | `--sf-space-unit` | Base spacing unit |
38
+ | `--sf-motion-duration` | Drawer motion (default `0ms`) |
39
+ | `--sf-color-focus` | Gallery focus ring (default `currentColor`) |
40
+ | `--sf-card-hover-lift` | Card hover lift (default `0px`) |
41
+ | `--sf-card-hover-shadow` | Card hover shadow (default `none`) |
42
+ | `--sf-card-image-hover-scale` | Card image hover zoom (default `1`) |
43
+ | `--sf-sold-opacity` | Sold card image opacity (default `0.55`) |
44
+ | `--sf-sold-filter` | Sold card image filter (default `grayscale(0.6)`) |
45
+ | `--sf-grid-columns-mobile` | Grid columns below 40rem (default `1`) |
46
+
47
+ ## Components
48
+
49
+ `PriceTag`, `AvailabilityBadge`, `ProductCard`, `ProductGrid`,
50
+ `CategoryFilter`, and `ProductGallery` are styled only through the `--sf-*`
51
+ variables. Wrap them in `StorefrontProvider` to supply framework adapters;
52
+ without it they fall back to plain `<a>` and `<img>`.
53
+
54
+ Example Next.js host setup (host code, not part of this package):
55
+
56
+ ```tsx
57
+ import Link from 'next/link';
58
+ import Image from 'next/image';
59
+ import { StorefrontProvider } from '@develemit/storefront';
60
+ import '@develemit/storefront/styles.css';
61
+
62
+ export function Providers({ children }: { children: React.ReactNode }) {
63
+ return (
64
+ <StorefrontProvider
65
+ locale="en-US"
66
+ renderLink={({ href, children, className }) => (
67
+ <Link href={href} className={className}>
68
+ {children}
69
+ </Link>
70
+ )}
71
+ renderImage={({ src, alt, sizes, className }) => (
72
+ <Image src={src} alt={alt} sizes={sizes} className={className} fill />
73
+ )}
74
+ hrefs={{
75
+ product: (slug) => `/shop/${slug}`,
76
+ category: (slug) => (slug ? `/shop?category=${slug}` : '/shop'),
77
+ }}
78
+ >
79
+ {children}
80
+ </StorefrontProvider>
81
+ );
82
+ }
83
+ ```
84
+
85
+ `AvailabilityBadge` and the components that embed it take a `labels` prop to
86
+ override the default copy ("One of a kind", "Sold", "On hold", "Available").
87
+ `CategoryFilter` renders buttons when `onSelect` is passed (controlled mode) and
88
+ links otherwise.
89
+
90
+ ## Server-safe helpers
91
+
92
+ The main entry carries a `"use client"` directive, so in React Server
93
+ Components its exports are client references and cannot be called. Pure helpers
94
+ live in a directive-free entry:
95
+
96
+ ```ts
97
+ import { formatPrice, sortForDisplay } from '@develemit/storefront/format';
98
+
99
+ formatPrice(2500, 'USD'); // "$25.00"
100
+ ```
101
+
102
+ Both are still exported from the main entry for client components.
103
+
104
+ ## API client
105
+
106
+ `createCommerceClient` lives in a separate, React-free entry so server code can
107
+ use it without pulling in the UI:
108
+
109
+ ```ts
110
+ import { createCommerceClient } from '@develemit/storefront/client';
111
+
112
+ const client = createCommerceClient({ baseUrl: 'https://example.com/api' });
113
+ await client.listProducts({ category: 'rings' });
114
+ await client.getProduct('sample-piece'); // throws NotFoundError on 404
115
+ await client.listCategories();
116
+ await client.createCheckout([{ productId: 'p1', quantity: 1 }]); // { url }
117
+ await client.getOrderBySession('cs_test_123'); // order summary
118
+ ```
119
+
120
+ `baseUrl` is where the commerce server plugins are mounted; the client appends
121
+ `/catalog/...`, `/checkout`, and `/orders/...`. Responses are parsed with the
122
+ contract schemas, so a drifting server fails loudly. Other non-2xx responses
123
+ throw `CommerceApiError`.
124
+
125
+ `createCheckout` throws `CheckoutConflictError` (a 409) when some items are
126
+ sold or held by another buyer; its `productIds` lists them so the cart can drop
127
+ them. Send only ids and quantities: the server prices every line itself.
128
+ Redirect the shopper to the returned `url`.
129
+
130
+ ## Views
131
+
132
+ `ShopView`, `ProductDetailView`, and `FeaturedProducts` take plain data plus
133
+ slots (`header`, `intro`, `emptyState`, `actions`, `afterDetails`).
134
+ `ShopView` lists sold items last (`sortForDisplay`).
135
+
136
+ ### Render a shop in a Next app
137
+
138
+ ```tsx
139
+ // app/shop/page.tsx (server component)
140
+ import { createCommerceClient } from '@develemit/storefront/client';
141
+ import { ShopView } from '@develemit/storefront';
142
+
143
+ const client = createCommerceClient({ baseUrl: process.env.COMMERCE_API_URL! });
144
+
145
+ export default async function ShopPage({
146
+ searchParams,
147
+ }: {
148
+ searchParams: Promise<{ category?: string }>;
149
+ }) {
150
+ const { category } = await searchParams;
151
+ const [products, categories] = await Promise.all([
152
+ client.listProducts({ category }),
153
+ client.listCategories(),
154
+ ]);
155
+ return (
156
+ <ShopView
157
+ products={products}
158
+ categories={categories}
159
+ activeCategory={category}
160
+ slots={{ header: <h1>Shop</h1> }}
161
+ />
162
+ );
163
+ }
164
+ ```
165
+
166
+ ```tsx
167
+ // app/shop/[slug]/page.tsx
168
+ import { notFound } from 'next/navigation';
169
+ import {
170
+ createCommerceClient,
171
+ NotFoundError,
172
+ } from '@develemit/storefront/client';
173
+ import { ProductDetailView } from '@develemit/storefront';
174
+
175
+ export default async function ProductPage({
176
+ params,
177
+ }: {
178
+ params: Promise<{ slug: string }>;
179
+ }) {
180
+ const { slug } = await params;
181
+ try {
182
+ const product = await client.getProduct(slug);
183
+ return <ProductDetailView product={product} />;
184
+ } catch (e) {
185
+ if (e instanceof NotFoundError) notFound();
186
+ throw e;
187
+ }
188
+ }
189
+ ```
190
+
191
+ ## Cart
192
+
193
+ Wrap the app in `CartProvider` (inside `StorefrontProvider`). State is a
194
+ snapshot of `CartLine`s; prices and availability are re-verified server-side
195
+ at checkout. Persistence goes through a `CartStorage` (`load`/`save`); the
196
+ default is `localStorageCartStorage(key)`, which hydrates after mount and never
197
+ touches `window` during render.
198
+
199
+ ```tsx
200
+ <CartProvider>
201
+ <CartButton />
202
+ <CartDrawer summary={{ onCheckout, checkoutLabel: 'Checkout' }} />
203
+ </CartProvider>
204
+
205
+ <ProductDetailView
206
+ product={product}
207
+ slots={{ actions: <AddToCartButton product={product} /> }}
208
+ />
209
+ ```
210
+
211
+ `useCart()` exposes `add` (returns `{ added, capped, reason? }`), `remove`,
212
+ `setQuantity`, `clear`, `open`/`close`, and the derived `itemCount` and
213
+ `subtotalCents`. A product's `quantity` is the line's `maxQuantity`, so
214
+ one-of-a-kind items can only be added once. Every label is overridable
215
+ through a `labels` prop.
216
+
217
+ ### Polish hooks
218
+
219
+ - `CartDrawer` `slots={{ empty, summaryNote }}`: `empty` replaces the
220
+ `labels.empty` text (for example with a link back to the shop); `summaryNote`
221
+ renders under the subtotal and Checkout button. An empty cart shows no
222
+ summary or Checkout button. Close and Remove are at least 44×44px.
223
+ - `CartButton` `icon` (rendered before the label), `labelHidden` (visually
224
+ hides the label, keeping an accessible name like "Cart, 2 items") and
225
+ `hideZeroCount` (omits the count at 0; the accessible name becomes
226
+ "Cart, empty").
227
+ - `--sf-motion-duration` (default `0ms`, no motion) drives the drawer's
228
+ slide-in and the backdrop's fade, in and out. It is forced to `0ms` under
229
+ `prefers-reduced-motion: reduce`. Example: `--sf-motion-duration: 200ms`.
230
+
231
+ ### Catalog presentation hooks
232
+
233
+ - Card hover: `--sf-card-hover-lift`, `--sf-card-hover-shadow` and
234
+ `--sf-card-image-hover-scale` apply on `:hover` and `:focus-within`, animated
235
+ with `--sf-motion-duration` (off under `prefers-reduced-motion: reduce`).
236
+ - Sold treatment: `--sf-sold-opacity` and `--sf-sold-filter` style the sold
237
+ card image.
238
+ - `--sf-grid-columns-mobile` sets the column count below 40rem.
239
+ - `CategoryFilter` `formatLabel(category)` and `sortCategories(a, b)` change
240
+ the label text and order (the input array is not mutated); `allLabel` renames
241
+ the All item. `ShopView` forwards them as `formatCategoryLabel`,
242
+ `sortCategories` and `allLabel`.
243
+ - `ShopView`, `FeaturedProducts` and `ProductDetailView` accept
244
+ `labels?: Partial<AvailabilityLabels>` for the availability badge.
245
+ - The gallery focus ring uses `--sf-color-focus`.
@@ -0,0 +1,25 @@
1
+ import { ReactNode } from 'react';
2
+ import { CartLine } from '@develemit/commerce-contracts';
3
+ import { AddResult } from './cart-reducer';
4
+ import { CartStorage } from './cart-storage';
5
+ export interface CartContextValue {
6
+ lines: CartLine[];
7
+ itemCount: number;
8
+ subtotalCents: number;
9
+ currency: string | null;
10
+ hydrated: boolean;
11
+ isOpen: boolean;
12
+ open: () => void;
13
+ close: () => void;
14
+ add: (line: CartLine) => AddResult;
15
+ remove: (productId: string) => void;
16
+ setQuantity: (productId: string, quantity: number) => void;
17
+ clear: () => void;
18
+ }
19
+ export interface CartProviderProps {
20
+ storage?: CartStorage;
21
+ children: ReactNode;
22
+ }
23
+ export declare function CartProvider({ storage, children, }: CartProviderProps): import('react').JSX.Element;
24
+ export declare function useCart(): CartContextValue;
25
+ //# sourceMappingURL=cart-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-provider.d.ts","sourceRoot":"","sources":["../../src/cart/cart-provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAML,KAAK,SAAS,EAEf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE3E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,GAAG,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,SAAS,CAAC;IACnC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAID,wBAAgB,YAAY,CAAC,EAC3B,OAAwB,EACxB,QAAQ,GACT,EAAE,iBAAiB,+BAsDnB;AAED,wBAAgB,OAAO,IAAI,gBAAgB,CAI1C"}
@@ -0,0 +1,32 @@
1
+ import { CartLine } from '@develemit/commerce-contracts';
2
+ export interface CartState {
3
+ lines: CartLine[];
4
+ }
5
+ export type CartAction = {
6
+ type: 'add';
7
+ line: CartLine;
8
+ } | {
9
+ type: 'remove';
10
+ productId: string;
11
+ } | {
12
+ type: 'setQuantity';
13
+ productId: string;
14
+ quantity: number;
15
+ } | {
16
+ type: 'clear';
17
+ } | {
18
+ type: 'hydrate';
19
+ state: CartState;
20
+ };
21
+ export interface AddResult {
22
+ state: CartState;
23
+ added: boolean;
24
+ capped: boolean;
25
+ reason?: 'currency-mismatch';
26
+ }
27
+ export declare const emptyCart: CartState;
28
+ export declare function addLine(state: CartState, line: CartLine): AddResult;
29
+ export declare function cartReducer(state: CartState, action: CartAction): CartState;
30
+ export declare function subtotalCents(state: CartState): number;
31
+ export declare function itemCount(state: CartState): number;
32
+ //# sourceMappingURL=cart-reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-reducer.d.ts","sourceRoot":"","sources":["../../src/cart/cart-reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,eAAO,MAAM,SAAS,EAAE,SAAyB,CAAC;AAElD,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,CA8BnE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CA4B3E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAElD"}
@@ -0,0 +1,7 @@
1
+ import { CartState } from './cart-reducer';
2
+ export interface CartStorage {
3
+ load: () => CartState | Promise<CartState>;
4
+ save: (state: CartState) => void | Promise<void>;
5
+ }
6
+ export declare function localStorageCartStorage(key?: string): CartStorage;
7
+ //# sourceMappingURL=cart-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-storage.d.ts","sourceRoot":"","sources":["../../src/cart/cart-storage.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3C,IAAI,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,wBAAgB,uBAAuB,CAAC,GAAG,SAAY,GAAG,WAAW,CA6BpE"}
@@ -0,0 +1,14 @@
1
+ import { Category, CheckoutRequest, CheckoutResponse, OrderSummary, Product, ProductListQuery } from '@develemit/commerce-contracts';
2
+ export interface CommerceClientOptions {
3
+ baseUrl: string;
4
+ fetch?: typeof fetch;
5
+ }
6
+ export interface CommerceClient {
7
+ listProducts(query?: ProductListQuery): Promise<Product[]>;
8
+ getProduct(slug: string): Promise<Product>;
9
+ listCategories(): Promise<Category[]>;
10
+ createCheckout(items: CheckoutRequest['items']): Promise<CheckoutResponse>;
11
+ getOrderBySession(sessionId: string): Promise<OrderSummary>;
12
+ }
13
+ export declare function createCommerceClient({ baseUrl, fetch: fetchImpl, }: CommerceClientOptions): CommerceClient;
14
+ //# sourceMappingURL=commerce-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commerce-client.d.ts","sourceRoot":"","sources":["../../src/client/commerce-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACtB,MAAM,+BAA+B,CAAC;AAOvC,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,KAAK,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3D,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,cAAc,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACtC,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3E,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7D;AAID,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,KAAK,EAAE,SAAkD,GAC1D,EAAE,qBAAqB,GAAG,cAAc,CAsExC"}
@@ -0,0 +1,13 @@
1
+ export declare class CommerceApiError extends Error {
2
+ readonly status: number;
3
+ readonly url: string;
4
+ constructor(status: number, url: string, message?: string);
5
+ }
6
+ export declare class NotFoundError extends CommerceApiError {
7
+ constructor(url: string);
8
+ }
9
+ export declare class CheckoutConflictError extends CommerceApiError {
10
+ readonly productIds: string[];
11
+ constructor(url: string, productIds: string[]);
12
+ }
13
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/client/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAiB,SAAQ,KAAK;IAEvC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM;gBADX,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM;CAKnB;AAED,qBAAa,aAAc,SAAQ,gBAAgB;gBACrC,GAAG,EAAE,MAAM;CAIxB;AAED,qBAAa,qBAAsB,SAAQ,gBAAgB;IAGvD,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE;gBAD7B,GAAG,EAAE,MAAM,EACF,UAAU,EAAE,MAAM,EAAE;CAShC"}
@@ -0,0 +1,3 @@
1
+ export * from './commerce-client';
2
+ export * from './errors';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,62 @@
1
+ import { categorySchema as e, checkoutConflictSchema as t, checkoutResponseSchema as n, orderSummarySchema as r, productSchema as i } from "@develemit/commerce-contracts";
2
+ // src/client/errors.ts
3
+ var a = class extends Error {
4
+ status;
5
+ url;
6
+ constructor(e, t, n) {
7
+ super(n ?? `Commerce API request failed (${e}): ${t}`), this.status = e, this.url = t, this.name = "CommerceApiError";
8
+ }
9
+ }, o = class extends a {
10
+ constructor(e) {
11
+ super(404, e, `Not found: ${e}`), this.name = "NotFoundError";
12
+ }
13
+ }, s = class extends a {
14
+ productIds;
15
+ constructor(e, t) {
16
+ super(409, e, `Some items are no longer available: ${t.join(", ")}`), this.productIds = t, this.name = "CheckoutConflictError";
17
+ }
18
+ };
19
+ // src/client/commerce-client.ts
20
+ function c({ baseUrl: c, fetch: l = (...e) => globalThis.fetch(...e) }) {
21
+ let u = c.replace(/\/+$/, "");
22
+ async function d(e, t, n) {
23
+ if (t.status === 404) throw new o(e);
24
+ if (!t.ok) throw new a(t.status, e);
25
+ return n.parse(await t.json());
26
+ }
27
+ async function f(e, t) {
28
+ let n = `${u}${e}`;
29
+ return d(n, await l(n, { headers: { accept: "application/json" } }), t);
30
+ }
31
+ async function p(e) {
32
+ let r = `${u}/checkout`, i = await l(r, {
33
+ method: "POST",
34
+ headers: {
35
+ accept: "application/json",
36
+ "content-type": "application/json"
37
+ },
38
+ body: JSON.stringify({ items: e })
39
+ });
40
+ if (i.status === 409) throw new s(r, t.parse(await i.json()).unavailableProductIds);
41
+ return d(r, i, n);
42
+ }
43
+ return {
44
+ listProducts(e = {}) {
45
+ let t = new URLSearchParams();
46
+ e.category && t.set("category", e.category), e.featured !== void 0 && t.set("featured", String(e.featured));
47
+ let n = t.toString();
48
+ return f(`/catalog/products${n ? `?${n}` : ""}`, i.array());
49
+ },
50
+ getProduct(e) {
51
+ return f(`/catalog/products/${encodeURIComponent(e)}`, i);
52
+ },
53
+ listCategories() {
54
+ return f("/catalog/categories", e.array());
55
+ },
56
+ createCheckout: p,
57
+ getOrderBySession(e) {
58
+ return f(`/orders/session/${encodeURIComponent(e)}`, r);
59
+ }
60
+ };
61
+ }
62
+ export { s as CheckoutConflictError, a as CommerceApiError, o as NotFoundError, c as createCommerceClient };
@@ -0,0 +1,15 @@
1
+ import { Product } from '@develemit/commerce-contracts';
2
+ export interface AddToCartLabels {
3
+ add: string;
4
+ inCart: string;
5
+ unavailable: string;
6
+ }
7
+ export declare const defaultAddToCartLabels: AddToCartLabels;
8
+ export interface AddToCartButtonProps {
9
+ product: Product;
10
+ labels?: Partial<AddToCartLabels>;
11
+ openOnAdd?: boolean;
12
+ className?: string;
13
+ }
14
+ export declare function AddToCartButton({ product, labels, openOnAdd, className, }: AddToCartButtonProps): import('react').JSX.Element;
15
+ //# sourceMappingURL=add-to-cart-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add-to-cart-button.d.ts","sourceRoot":"","sources":["../../../src/components/add-to-cart-button/add-to-cart-button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAG7D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,sBAAsB,EAAE,eAIpC,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,MAAM,EACN,SAAgB,EAChB,SAAS,GACV,EAAE,oBAAoB,+BAkCtB"}
@@ -0,0 +1,16 @@
1
+ import { ProductAvailability } from '@develemit/commerce-contracts';
2
+ export interface AvailabilityLabels {
3
+ oneOfAKind: string;
4
+ sold: string;
5
+ reserved: string;
6
+ available: string;
7
+ }
8
+ export declare const defaultAvailabilityLabels: AvailabilityLabels;
9
+ export interface AvailabilityBadgeProps {
10
+ availability: ProductAvailability;
11
+ quantity: number;
12
+ labels?: Partial<AvailabilityLabels>;
13
+ className?: string;
14
+ }
15
+ export declare function AvailabilityBadge({ availability, quantity, labels, className, }: AvailabilityBadgeProps): import('react').JSX.Element;
16
+ //# sourceMappingURL=availability-badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"availability-badge.d.ts","sourceRoot":"","sources":["../../../src/components/availability-badge/availability-badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,yBAAyB,EAAE,kBAKvC,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,YAAY,EAAE,mBAAmB,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE,sBAAsB,+BAyBxB"}
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ export interface CartButtonProps {
3
+ label?: string;
4
+ className?: string;
5
+ icon?: ReactNode;
6
+ labelHidden?: boolean;
7
+ hideZeroCount?: boolean;
8
+ }
9
+ export declare function CartButton({ label, className, icon, labelHidden, hideZeroCount, }: CartButtonProps): import('react').JSX.Element;
10
+ //# sourceMappingURL=cart-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-button.d.ts","sourceRoot":"","sources":["../../../src/components/cart-button/cart-button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,UAAU,CAAC,EACzB,KAAc,EACd,SAAS,EACT,IAAI,EACJ,WAAmB,EACnB,aAAqB,GACtB,EAAE,eAAe,+BA+BjB"}
@@ -0,0 +1,19 @@
1
+ import { ReactNode } from 'react';
2
+ import { CartSummaryProps } from '../cart-summary/cart-summary';
3
+ export interface CartDrawerLabels {
4
+ title: string;
5
+ close: string;
6
+ empty: string;
7
+ }
8
+ export declare const defaultCartDrawerLabels: CartDrawerLabels;
9
+ export interface CartDrawerProps {
10
+ labels?: Partial<CartDrawerLabels>;
11
+ summary?: CartSummaryProps;
12
+ slots?: CartDrawerSlots;
13
+ }
14
+ export interface CartDrawerSlots {
15
+ empty?: ReactNode;
16
+ summaryNote?: ReactNode;
17
+ }
18
+ export declare function CartDrawer({ labels, summary, slots }: CartDrawerProps): import('react').JSX.Element | null;
19
+ //# sourceMappingURL=cart-drawer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-drawer.d.ts","sourceRoot":"","sources":["../../../src/components/cart-drawer/cart-drawer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI9E,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,uBAAuB,EAAE,gBAIrC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,SAAS,CAAC;CACzB;AAKD,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,eAAe,sCAyFrE"}
@@ -0,0 +1,6 @@
1
+ import { RefObject } from 'react';
2
+ export declare function useDrawerPresence(isOpen: boolean, backdropRef: RefObject<HTMLElement | null>): {
3
+ mounted: boolean;
4
+ state: string;
5
+ };
6
+ //# sourceMappingURL=use-drawer-presence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-drawer-presence.d.ts","sourceRoot":"","sources":["../../../src/components/cart-drawer/use-drawer-presence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG5D,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC;;;EA6B3C"}
@@ -0,0 +1,12 @@
1
+ import { CartLine } from '@develemit/commerce-contracts';
2
+ export interface CartLineItemLabels {
3
+ remove: string;
4
+ quantity: string;
5
+ }
6
+ export declare const defaultCartLineItemLabels: CartLineItemLabels;
7
+ export interface CartLineItemProps {
8
+ line: CartLine;
9
+ labels?: Partial<CartLineItemLabels>;
10
+ }
11
+ export declare function CartLineItem({ line, labels }: CartLineItemProps): import('react').JSX.Element;
12
+ //# sourceMappingURL=cart-line-item.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-line-item.d.ts","sourceRoot":"","sources":["../../../src/components/cart-line-item/cart-line-item.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAK9D,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,yBAAyB,EAAE,kBAGvC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACtC;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,iBAAiB,+BAgD/D"}
@@ -0,0 +1,11 @@
1
+ export interface CartSummaryLabels {
2
+ subtotal: string;
3
+ }
4
+ export interface CartSummaryProps {
5
+ onCheckout?: () => void;
6
+ checkoutLabel?: string;
7
+ checkoutDisabled?: boolean;
8
+ labels?: Partial<CartSummaryLabels>;
9
+ }
10
+ export declare function CartSummary({ onCheckout, checkoutLabel, checkoutDisabled, labels, }: CartSummaryProps): import('react').JSX.Element;
11
+ //# sourceMappingURL=cart-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-summary.d.ts","sourceRoot":"","sources":["../../../src/components/cart-summary/cart-summary.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACrC;AAED,wBAAgB,WAAW,CAAC,EAC1B,UAAU,EACV,aAA0B,EAC1B,gBAAwB,EACxB,MAAM,GACP,EAAE,gBAAgB,+BAsBlB"}
@@ -0,0 +1,12 @@
1
+ import { Category } from '@develemit/commerce-contracts';
2
+ export interface CategoryFilterProps {
3
+ categories: Category[];
4
+ selected?: string;
5
+ onSelect?: (slug: string | undefined) => void;
6
+ allLabel?: string;
7
+ label?: string;
8
+ formatLabel?: (category: Category) => string;
9
+ sortCategories?: (a: Category, b: Category) => number;
10
+ }
11
+ export declare function CategoryFilter({ categories, selected, onSelect, allLabel, label, formatLabel, sortCategories, }: CategoryFilterProps): import('react').JSX.Element;
12
+ //# sourceMappingURL=category-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"category-filter.d.ts","sourceRoot":"","sources":["../../../src/components/category-filter/category-filter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAG9D,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,CAAC;IAC7C,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,KAAK,MAAM,CAAC;CACvD;AAED,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,QAAgB,EAChB,KAAoB,EACpB,WAAW,EACX,cAAc,GACf,EAAE,mBAAmB,+BA8CrB"}
@@ -0,0 +1,3 @@
1
+ import { Product } from '@develemit/commerce-contracts';
2
+ export declare function makeProduct(overrides?: Partial<Product>): Product;
3
+ //# sourceMappingURL=fixtures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures.d.ts","sourceRoot":"","sources":["../../src/components/fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAE7D,wBAAgB,WAAW,CAAC,SAAS,GAAE,OAAO,CAAC,OAAO,CAAM,GAAG,OAAO,CAerE"}
@@ -0,0 +1,8 @@
1
+ export interface PriceTagProps {
2
+ priceCents: number;
3
+ currency: string;
4
+ sold?: boolean;
5
+ className?: string;
6
+ }
7
+ export declare function PriceTag({ priceCents, currency, sold, className, }: PriceTagProps): import('react').JSX.Element;
8
+ //# sourceMappingURL=price-tag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price-tag.d.ts","sourceRoot":"","sources":["../../../src/components/price-tag/price-tag.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,QAAQ,CAAC,EACvB,UAAU,EACV,QAAQ,EACR,IAAY,EACZ,SAAS,GACV,EAAE,aAAa,+BAQf"}
@@ -0,0 +1,10 @@
1
+ import { Product } from '@develemit/commerce-contracts';
2
+ import { AvailabilityLabels } from '../availability-badge/availability-badge';
3
+ export interface ProductCardProps {
4
+ product: Product;
5
+ labels?: Partial<AvailabilityLabels>;
6
+ sizes?: string;
7
+ priority?: boolean;
8
+ }
9
+ export declare function ProductCard({ product, labels, sizes, priority, }: ProductCardProps): import('react').JSX.Element;
10
+ //# sourceMappingURL=product-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-card.d.ts","sourceRoot":"","sources":["../../../src/components/product-card/product-card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,0CAA0C,CAAC;AAGlD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,MAAM,EACN,KAAK,EACL,QAAQ,GACT,EAAE,gBAAgB,+BA6ClB"}
@@ -0,0 +1,7 @@
1
+ import { ProductImage } from '@develemit/commerce-contracts';
2
+ export interface ProductGalleryProps {
3
+ images: ProductImage[];
4
+ sizes?: string;
5
+ }
6
+ export declare function ProductGallery({ images, sizes }: ProductGalleryProps): import('react').JSX.Element | null;
7
+ //# sourceMappingURL=product-gallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-gallery.d.ts","sourceRoot":"","sources":["../../../src/components/product-gallery/product-gallery.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAGlE,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,mBAAmB,sCAqEpE"}