@marianmeres/ecsuite 1.0.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 (49) hide show
  1. package/AGENTS.md +218 -0
  2. package/API.md +865 -0
  3. package/LICENSE +21 -0
  4. package/README.md +149 -0
  5. package/dist/adapters/mock/cart.d.ts +20 -0
  6. package/dist/adapters/mock/cart.js +86 -0
  7. package/dist/adapters/mock/customer.d.ts +20 -0
  8. package/dist/adapters/mock/customer.js +58 -0
  9. package/dist/adapters/mock/mod.d.ts +9 -0
  10. package/dist/adapters/mock/mod.js +9 -0
  11. package/dist/adapters/mock/order.d.ts +20 -0
  12. package/dist/adapters/mock/order.js +66 -0
  13. package/dist/adapters/mock/payment.d.ts +20 -0
  14. package/dist/adapters/mock/payment.js +55 -0
  15. package/dist/adapters/mock/product.d.ts +25 -0
  16. package/dist/adapters/mock/product.js +60 -0
  17. package/dist/adapters/mock/wishlist.d.ts +20 -0
  18. package/dist/adapters/mock/wishlist.js +70 -0
  19. package/dist/adapters/mod.d.ts +6 -0
  20. package/dist/adapters/mod.js +6 -0
  21. package/dist/domains/base.d.ts +83 -0
  22. package/dist/domains/base.js +187 -0
  23. package/dist/domains/cart.d.ts +96 -0
  24. package/dist/domains/cart.js +287 -0
  25. package/dist/domains/customer.d.ts +74 -0
  26. package/dist/domains/customer.js +183 -0
  27. package/dist/domains/mod.d.ts +13 -0
  28. package/dist/domains/mod.js +13 -0
  29. package/dist/domains/order.d.ts +83 -0
  30. package/dist/domains/order.js +233 -0
  31. package/dist/domains/payment.d.ts +83 -0
  32. package/dist/domains/payment.js +175 -0
  33. package/dist/domains/product.d.ts +130 -0
  34. package/dist/domains/product.js +241 -0
  35. package/dist/domains/wishlist.d.ts +101 -0
  36. package/dist/domains/wishlist.js +256 -0
  37. package/dist/mod.d.ts +28 -0
  38. package/dist/mod.js +32 -0
  39. package/dist/suite.d.ts +115 -0
  40. package/dist/suite.js +168 -0
  41. package/dist/types/adapter.d.ts +77 -0
  42. package/dist/types/adapter.js +7 -0
  43. package/dist/types/events.d.ts +111 -0
  44. package/dist/types/events.js +7 -0
  45. package/dist/types/mod.d.ts +9 -0
  46. package/dist/types/mod.js +9 -0
  47. package/dist/types/state.d.ts +61 -0
  48. package/dist/types/state.js +7 -0
  49. package/package.json +28 -0
@@ -0,0 +1,241 @@
1
+ /**
2
+ * @module domains/product
3
+ *
4
+ * Product domain manager with in-memory caching.
5
+ *
6
+ * Read-only domain for fetching product data with caching support.
7
+ * Does not use state machine - just a simple cache layer.
8
+ */
9
+ import { createClog } from "@marianmeres/clog";
10
+ import { createPubSub } from "@marianmeres/pubsub";
11
+ /**
12
+ * Product manager with in-memory caching.
13
+ *
14
+ * Unlike other domain managers, ProductManager uses a simple cache layer
15
+ * instead of a full state machine. Products are fetched on-demand and cached
16
+ * with a configurable TTL.
17
+ *
18
+ * Features:
19
+ * - In-memory cache with TTL expiration
20
+ * - Single and batch product fetching
21
+ * - Prefetching support for UI optimization
22
+ * - Event emission on fetch
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const products = new ProductManager({
27
+ * adapter: myProductAdapter,
28
+ * cacheTtl: 10 * 60 * 1000, // 10 minutes
29
+ * });
30
+ *
31
+ * const product = await products.getById("prod-123");
32
+ * const many = await products.getByIds(["prod-1", "prod-2"]);
33
+ * ```
34
+ */
35
+ export class ProductManager {
36
+ _clog = createClog("ecsuite:product", { color: "auto" });
37
+ _pubsub;
38
+ _adapter = null;
39
+ _context = {};
40
+ _cache = new Map();
41
+ _cacheTtl;
42
+ constructor(options = {}) {
43
+ this._adapter = options.adapter ?? null;
44
+ this._context = options.context ?? {};
45
+ this._pubsub = options.pubsub ?? createPubSub();
46
+ this._cacheTtl = options.cacheTtl ?? 5 * 60 * 1000; // 5 minutes default
47
+ this._clog.debug("initialized", { cacheTtl: this._cacheTtl });
48
+ }
49
+ /**
50
+ * Set the product adapter for server communication.
51
+ *
52
+ * @param adapter - The ProductAdapter implementation
53
+ */
54
+ setAdapter(adapter) {
55
+ this._adapter = adapter;
56
+ this._clog.debug("adapter set");
57
+ }
58
+ /**
59
+ * Get the current adapter.
60
+ *
61
+ * @returns The adapter or null if not set
62
+ */
63
+ getAdapter() {
64
+ return this._adapter;
65
+ }
66
+ /**
67
+ * Update context (customerId, sessionId).
68
+ *
69
+ * @param context - Context to merge with existing context
70
+ */
71
+ setContext(context) {
72
+ this._context = { ...this._context, ...context };
73
+ }
74
+ /**
75
+ * Get the current context.
76
+ *
77
+ * @returns Copy of the current context
78
+ */
79
+ getContext() {
80
+ return { ...this._context };
81
+ }
82
+ /**
83
+ * Get a single product by ID.
84
+ * Returns from cache if valid, otherwise fetches from server.
85
+ *
86
+ * @param productId - The product ID to fetch
87
+ * @returns The product data or null if not found/error
88
+ * @emits product:fetched - On successful server fetch
89
+ */
90
+ async getById(productId) {
91
+ // Check cache first
92
+ const cached = this._getFromCache(productId);
93
+ if (cached) {
94
+ return cached;
95
+ }
96
+ // Fetch from server
97
+ if (!this._adapter) {
98
+ this._clog.debug("getById: no adapter", { productId });
99
+ return null;
100
+ }
101
+ this._clog.debug("getById: fetching", { productId });
102
+ try {
103
+ const result = await this._adapter.fetchOne(productId, this._context);
104
+ if (result.success && result.data) {
105
+ this._setCache(productId, result.data);
106
+ this._emitFetched(productId);
107
+ return result.data;
108
+ }
109
+ return null;
110
+ }
111
+ catch (e) {
112
+ this._clog.error("getById failed", { productId, error: e });
113
+ return null;
114
+ }
115
+ }
116
+ /**
117
+ * Get multiple products by IDs.
118
+ * Returns from cache when available, fetches missing products in batch.
119
+ *
120
+ * @param productIds - Array of product IDs to fetch
121
+ * @returns Map of productId to ProductData
122
+ * @emits product:fetched - For each product fetched from server
123
+ */
124
+ async getByIds(productIds) {
125
+ const result = new Map();
126
+ const missingIds = [];
127
+ // Check cache for each product
128
+ for (const id of productIds) {
129
+ const cached = this._getFromCache(id);
130
+ if (cached) {
131
+ result.set(id, cached);
132
+ }
133
+ else {
134
+ missingIds.push(id);
135
+ }
136
+ }
137
+ // Fetch missing from server
138
+ if (missingIds.length > 0 && this._adapter) {
139
+ this._clog.debug("getByIds: fetching missing", {
140
+ total: productIds.length,
141
+ cached: result.size,
142
+ missing: missingIds.length,
143
+ });
144
+ try {
145
+ const fetchResult = await this._adapter.fetchMany(missingIds, this._context);
146
+ if (fetchResult.success && fetchResult.data) {
147
+ for (const product of fetchResult.data) {
148
+ // Products from collection-types have model_id
149
+ const productId = product.model_id;
150
+ if (productId) {
151
+ this._setCache(productId, product);
152
+ result.set(productId, product);
153
+ this._emitFetched(productId);
154
+ }
155
+ }
156
+ }
157
+ }
158
+ catch (e) {
159
+ this._clog.error("getByIds failed", { missingIds, error: e });
160
+ }
161
+ }
162
+ return result;
163
+ }
164
+ /**
165
+ * Prefetch products into cache.
166
+ * Useful for preloading product data before rendering.
167
+ *
168
+ * @param productIds - Array of product IDs to prefetch
169
+ */
170
+ async prefetch(productIds) {
171
+ const missingIds = productIds.filter((id) => !this.isCached(id));
172
+ if (missingIds.length === 0)
173
+ return;
174
+ this._clog.debug("prefetch", { count: missingIds.length });
175
+ await this.getByIds(missingIds);
176
+ }
177
+ /**
178
+ * Clear the product cache entirely or for a specific product.
179
+ *
180
+ * @param productId - Optional product ID to clear (clears all if not provided)
181
+ */
182
+ clearCache(productId) {
183
+ if (productId) {
184
+ this._cache.delete(productId);
185
+ this._clog.debug("cache cleared for product", { productId });
186
+ }
187
+ else {
188
+ this._cache.clear();
189
+ this._clog.debug("cache cleared entirely");
190
+ }
191
+ }
192
+ /**
193
+ * Check if a product is in the cache and not expired.
194
+ *
195
+ * @param productId - The product ID to check
196
+ * @returns True if the product is cached and valid
197
+ */
198
+ isCached(productId) {
199
+ const entry = this._cache.get(productId);
200
+ if (!entry)
201
+ return false;
202
+ return Date.now() < entry.expiresAt;
203
+ }
204
+ /**
205
+ * Get the current cache size.
206
+ *
207
+ * @returns Number of cached products (includes expired entries)
208
+ */
209
+ getCacheSize() {
210
+ return this._cache.size;
211
+ }
212
+ /** Get product from cache if valid */
213
+ _getFromCache(productId) {
214
+ const entry = this._cache.get(productId);
215
+ if (!entry)
216
+ return null;
217
+ if (Date.now() >= entry.expiresAt) {
218
+ // Expired, remove from cache
219
+ this._cache.delete(productId);
220
+ return null;
221
+ }
222
+ return entry.data;
223
+ }
224
+ /** Set product in cache */
225
+ _setCache(productId, data) {
226
+ this._cache.set(productId, {
227
+ data,
228
+ expiresAt: Date.now() + this._cacheTtl,
229
+ });
230
+ }
231
+ /** Emit product:fetched event */
232
+ _emitFetched(productId) {
233
+ const event = {
234
+ type: "product:fetched",
235
+ domain: "product",
236
+ timestamp: Date.now(),
237
+ productId,
238
+ };
239
+ this._pubsub.publish(event.type, event);
240
+ }
241
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @module domains/wishlist
3
+ *
4
+ * Wishlist domain manager with optimistic updates and localStorage persistence.
5
+ * Manages wishlist state with automatic server synchronization.
6
+ */
7
+ import type { UUID } from "@marianmeres/collection-types";
8
+ import type { WishlistAdapter } from "../types/adapter.js";
9
+ import type { EnrichedWishlistItem, WishlistData, WishlistItem } from "../types/state.js";
10
+ import { BaseDomainManager, type BaseDomainOptions } from "./base.js";
11
+ import type { ProductManager } from "./product.js";
12
+ export interface WishlistManagerOptions extends BaseDomainOptions {
13
+ /** Wishlist adapter for server communication */
14
+ adapter?: WishlistAdapter;
15
+ }
16
+ /**
17
+ * Wishlist domain manager with optimistic updates and localStorage persistence.
18
+ *
19
+ * Features:
20
+ * - Automatic localStorage persistence (configurable)
21
+ * - Optimistic updates with automatic rollback on server error
22
+ * - Server synchronization via WishlistAdapter
23
+ * - Toggle functionality for easy add/remove
24
+ * - Enriched items with product data support
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const wishlist = new WishlistManager({ adapter: myWishlistAdapter });
29
+ * await wishlist.initialize();
30
+ *
31
+ * await wishlist.toggleItem("prod-1"); // Adds item
32
+ * await wishlist.toggleItem("prod-1"); // Removes item
33
+ * ```
34
+ */
35
+ export declare class WishlistManager extends BaseDomainManager<WishlistData, WishlistAdapter> {
36
+ constructor(options?: WishlistManagerOptions);
37
+ /** Initialize wishlist (load from storage, then sync with server) */
38
+ initialize(): Promise<void>;
39
+ /**
40
+ * Add a product to the wishlist.
41
+ * No-op if the product is already in the wishlist.
42
+ *
43
+ * @param productId - The product ID to add
44
+ * @emits wishlist:item:added - On successful addition
45
+ */
46
+ addItem(productId: UUID): Promise<void>;
47
+ /**
48
+ * Remove a product from the wishlist.
49
+ *
50
+ * @param productId - The product ID to remove
51
+ * @emits wishlist:item:removed - On successful removal
52
+ */
53
+ removeItem(productId: UUID): Promise<void>;
54
+ /**
55
+ * Toggle a product in the wishlist.
56
+ * Adds the product if not present, removes it if present.
57
+ *
58
+ * @param productId - The product ID to toggle
59
+ * @returns True if the item was added, false if removed
60
+ */
61
+ toggleItem(productId: UUID): Promise<boolean>;
62
+ /**
63
+ * Clear all items from the wishlist.
64
+ *
65
+ * @emits wishlist:cleared - On successful clear
66
+ */
67
+ clear(): Promise<void>;
68
+ /**
69
+ * Get the total number of items in the wishlist.
70
+ *
71
+ * @returns Total item count
72
+ */
73
+ getItemCount(): number;
74
+ /**
75
+ * Check if a product is in the wishlist.
76
+ *
77
+ * @param productId - The product ID to check
78
+ * @returns True if the product is in the wishlist
79
+ */
80
+ hasProduct(productId: UUID): boolean;
81
+ /**
82
+ * Get a wishlist item by product ID.
83
+ *
84
+ * @param productId - The product ID to find
85
+ * @returns The wishlist item or undefined if not found
86
+ */
87
+ getItem(productId: UUID): WishlistItem | undefined;
88
+ /**
89
+ * Get all product IDs in the wishlist.
90
+ *
91
+ * @returns Array of product IDs
92
+ */
93
+ getProductIds(): UUID[];
94
+ /**
95
+ * Get wishlist items enriched with product data.
96
+ *
97
+ * @param productManager - The ProductManager to fetch product data from
98
+ * @returns Array of enriched wishlist items with product data
99
+ */
100
+ getEnrichedItems(productManager: ProductManager): Promise<EnrichedWishlistItem[]>;
101
+ }
@@ -0,0 +1,256 @@
1
+ /**
2
+ * @module domains/wishlist
3
+ *
4
+ * Wishlist domain manager with optimistic updates and localStorage persistence.
5
+ * Manages wishlist state with automatic server synchronization.
6
+ */
7
+ import { BaseDomainManager } from "./base.js";
8
+ /**
9
+ * Wishlist domain manager with optimistic updates and localStorage persistence.
10
+ *
11
+ * Features:
12
+ * - Automatic localStorage persistence (configurable)
13
+ * - Optimistic updates with automatic rollback on server error
14
+ * - Server synchronization via WishlistAdapter
15
+ * - Toggle functionality for easy add/remove
16
+ * - Enriched items with product data support
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const wishlist = new WishlistManager({ adapter: myWishlistAdapter });
21
+ * await wishlist.initialize();
22
+ *
23
+ * await wishlist.toggleItem("prod-1"); // Adds item
24
+ * await wishlist.toggleItem("prod-1"); // Removes item
25
+ * ```
26
+ */
27
+ export class WishlistManager extends BaseDomainManager {
28
+ constructor(options = {}) {
29
+ super("wishlist", {
30
+ ...options,
31
+ // Wishlist defaults to localStorage persistence
32
+ storageKey: options.storageKey ?? "ecsuite:wishlist",
33
+ storageType: options.storageType ?? "local",
34
+ });
35
+ if (options.adapter) {
36
+ this._adapter = options.adapter;
37
+ }
38
+ }
39
+ /** Initialize wishlist (load from storage, then sync with server) */
40
+ async initialize() {
41
+ this._clog.debug("initialize start");
42
+ const current = this._store.get();
43
+ // If we have persisted data, use it immediately
44
+ if (current.data) {
45
+ this._setState("ready");
46
+ }
47
+ // Then sync with server if adapter is available
48
+ if (this._adapter) {
49
+ this._setState("syncing");
50
+ try {
51
+ const result = await this._adapter.fetch(this._context);
52
+ if (result.success && result.data) {
53
+ this._setData(result.data);
54
+ this._markSynced();
55
+ }
56
+ else if (result.error) {
57
+ this._setError({
58
+ code: result.error.code,
59
+ message: result.error.message,
60
+ operation: "initialize",
61
+ });
62
+ }
63
+ }
64
+ catch (e) {
65
+ this._setError({
66
+ code: "FETCH_FAILED",
67
+ message: e instanceof Error ? e.message : "Failed to fetch wishlist",
68
+ originalError: e,
69
+ operation: "initialize",
70
+ });
71
+ }
72
+ }
73
+ else {
74
+ // No adapter, just use local storage or create empty wishlist
75
+ if (!current.data) {
76
+ this._setData({ items: [] });
77
+ }
78
+ this._setState("ready");
79
+ }
80
+ this._clog.debug("initialize complete", { itemCount: this.getItemCount() });
81
+ }
82
+ /**
83
+ * Add a product to the wishlist.
84
+ * No-op if the product is already in the wishlist.
85
+ *
86
+ * @param productId - The product ID to add
87
+ * @emits wishlist:item:added - On successful addition
88
+ */
89
+ async addItem(productId) {
90
+ this._clog.debug("addItem", { productId });
91
+ // Check if already in wishlist
92
+ const current = this._store.get().data ?? { items: [] };
93
+ if (current.items.some((i) => i.product_id === productId)) {
94
+ return; // Already in wishlist, no-op
95
+ }
96
+ const newItem = {
97
+ product_id: productId,
98
+ added_at: Date.now(),
99
+ };
100
+ await this._withOptimisticUpdate("addItem", () => {
101
+ const items = [...current.items, newItem];
102
+ this._setData({ items }, false);
103
+ }, async () => {
104
+ if (this._adapter) {
105
+ const result = await this._adapter.addItem(productId, this._context);
106
+ if (!result.success) {
107
+ throw new Error(result.error?.message ?? "Failed to add item");
108
+ }
109
+ return result.data;
110
+ }
111
+ return this._store.get().data;
112
+ }, (serverData) => {
113
+ if (serverData) {
114
+ this._setData(serverData);
115
+ }
116
+ this._emit({
117
+ type: "wishlist:item:added",
118
+ domain: "wishlist",
119
+ timestamp: Date.now(),
120
+ productId,
121
+ });
122
+ });
123
+ }
124
+ /**
125
+ * Remove a product from the wishlist.
126
+ *
127
+ * @param productId - The product ID to remove
128
+ * @emits wishlist:item:removed - On successful removal
129
+ */
130
+ async removeItem(productId) {
131
+ this._clog.debug("removeItem", { productId });
132
+ await this._withOptimisticUpdate("removeItem", () => {
133
+ const current = this._store.get().data ?? { items: [] };
134
+ const newItems = current.items.filter((i) => i.product_id !== productId);
135
+ this._setData({ items: newItems }, false);
136
+ }, async () => {
137
+ if (this._adapter) {
138
+ const result = await this._adapter.removeItem(productId, this._context);
139
+ if (!result.success) {
140
+ throw new Error(result.error?.message ?? "Failed to remove item");
141
+ }
142
+ return result.data;
143
+ }
144
+ return this._store.get().data;
145
+ }, (serverData) => {
146
+ if (serverData) {
147
+ this._setData(serverData);
148
+ }
149
+ this._emit({
150
+ type: "wishlist:item:removed",
151
+ domain: "wishlist",
152
+ timestamp: Date.now(),
153
+ productId,
154
+ });
155
+ });
156
+ }
157
+ /**
158
+ * Toggle a product in the wishlist.
159
+ * Adds the product if not present, removes it if present.
160
+ *
161
+ * @param productId - The product ID to toggle
162
+ * @returns True if the item was added, false if removed
163
+ */
164
+ async toggleItem(productId) {
165
+ this._clog.debug("toggleItem", { productId });
166
+ if (this.hasProduct(productId)) {
167
+ await this.removeItem(productId);
168
+ return false;
169
+ }
170
+ else {
171
+ await this.addItem(productId);
172
+ return true;
173
+ }
174
+ }
175
+ /**
176
+ * Clear all items from the wishlist.
177
+ *
178
+ * @emits wishlist:cleared - On successful clear
179
+ */
180
+ async clear() {
181
+ this._clog.debug("clear");
182
+ await this._withOptimisticUpdate("clear", () => {
183
+ this._setData({ items: [] }, false);
184
+ }, async () => {
185
+ if (this._adapter) {
186
+ const result = await this._adapter.clear(this._context);
187
+ if (!result.success) {
188
+ throw new Error(result.error?.message ?? "Failed to clear wishlist");
189
+ }
190
+ return result.data;
191
+ }
192
+ return { items: [] };
193
+ }, () => {
194
+ this._emit({
195
+ type: "wishlist:cleared",
196
+ domain: "wishlist",
197
+ timestamp: Date.now(),
198
+ });
199
+ });
200
+ }
201
+ /**
202
+ * Get the total number of items in the wishlist.
203
+ *
204
+ * @returns Total item count
205
+ */
206
+ getItemCount() {
207
+ const data = this._store.get().data;
208
+ return data?.items.length ?? 0;
209
+ }
210
+ /**
211
+ * Check if a product is in the wishlist.
212
+ *
213
+ * @param productId - The product ID to check
214
+ * @returns True if the product is in the wishlist
215
+ */
216
+ hasProduct(productId) {
217
+ const data = this._store.get().data;
218
+ return data?.items.some((i) => i.product_id === productId) ?? false;
219
+ }
220
+ /**
221
+ * Get a wishlist item by product ID.
222
+ *
223
+ * @param productId - The product ID to find
224
+ * @returns The wishlist item or undefined if not found
225
+ */
226
+ getItem(productId) {
227
+ const data = this._store.get().data;
228
+ return data?.items.find((i) => i.product_id === productId);
229
+ }
230
+ /**
231
+ * Get all product IDs in the wishlist.
232
+ *
233
+ * @returns Array of product IDs
234
+ */
235
+ getProductIds() {
236
+ const data = this._store.get().data;
237
+ return data?.items.map((i) => i.product_id) ?? [];
238
+ }
239
+ /**
240
+ * Get wishlist items enriched with product data.
241
+ *
242
+ * @param productManager - The ProductManager to fetch product data from
243
+ * @returns Array of enriched wishlist items with product data
244
+ */
245
+ async getEnrichedItems(productManager) {
246
+ const data = this._store.get().data;
247
+ if (!data?.items.length)
248
+ return [];
249
+ const productIds = data.items.map((i) => i.product_id);
250
+ const products = await productManager.getByIds(productIds);
251
+ return data.items.map((item) => ({
252
+ ...item,
253
+ product: products.get(item.product_id) ?? null,
254
+ }));
255
+ }
256
+ }
package/dist/mod.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @module @marianmeres/ecsuite
3
+ *
4
+ * E-commerce frontend UI helper library with optimistic updates,
5
+ * Svelte-compatible stores, and adapter-based server sync.
6
+ *
7
+ * @example Basic usage
8
+ * ```typescript
9
+ * import { createECSuite } from "@marianmeres/ecsuite";
10
+ *
11
+ * const suite = createECSuite({
12
+ * context: { customerId: "user-123" },
13
+ * adapters: { cart: myCartAdapter },
14
+ * });
15
+ *
16
+ * // Subscribe to cart state (Svelte-compatible)
17
+ * suite.cart.subscribe((state) => {
18
+ * console.log(state.state, state.data);
19
+ * });
20
+ *
21
+ * // Add item with optimistic update
22
+ * await suite.cart.addItem({ product_id: "prod-1", quantity: 2 });
23
+ * ```
24
+ */
25
+ export { ECSuite, createECSuite, type ECSuiteConfig } from "./suite.js";
26
+ export * from "./types/mod.js";
27
+ export * from "./domains/mod.js";
28
+ export * from "./adapters/mod.js";
package/dist/mod.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @module @marianmeres/ecsuite
3
+ *
4
+ * E-commerce frontend UI helper library with optimistic updates,
5
+ * Svelte-compatible stores, and adapter-based server sync.
6
+ *
7
+ * @example Basic usage
8
+ * ```typescript
9
+ * import { createECSuite } from "@marianmeres/ecsuite";
10
+ *
11
+ * const suite = createECSuite({
12
+ * context: { customerId: "user-123" },
13
+ * adapters: { cart: myCartAdapter },
14
+ * });
15
+ *
16
+ * // Subscribe to cart state (Svelte-compatible)
17
+ * suite.cart.subscribe((state) => {
18
+ * console.log(state.state, state.data);
19
+ * });
20
+ *
21
+ * // Add item with optimistic update
22
+ * await suite.cart.addItem({ product_id: "prod-1", quantity: 2 });
23
+ * ```
24
+ */
25
+ // Main exports
26
+ export { ECSuite, createECSuite } from "./suite.js";
27
+ // Types
28
+ export * from "./types/mod.js";
29
+ // Domain managers (for advanced usage)
30
+ export * from "./domains/mod.js";
31
+ // Adapters (including mock adapters for testing)
32
+ export * from "./adapters/mod.js";