@marianmeres/ecsuite 1.3.2 → 2.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.
- package/AGENTS.md +47 -12
- package/API.md +108 -25
- package/README.md +70 -0
- package/dist/adapters/mock/cart.d.ts +4 -2
- package/dist/adapters/mock/cart.js +3 -7
- package/dist/adapters/mock/customer.d.ts +3 -1
- package/dist/adapters/mock/customer.js +3 -2
- package/dist/adapters/mock/order.d.ts +3 -1
- package/dist/adapters/mock/order.js +7 -5
- package/dist/adapters/mock/payment.d.ts +3 -1
- package/dist/adapters/mock/payment.js +34 -20
- package/dist/adapters/mock/product.d.ts +3 -1
- package/dist/adapters/mock/product.js +3 -1
- package/dist/adapters/mock/wishlist.d.ts +4 -2
- package/dist/adapters/mock/wishlist.js +3 -7
- package/dist/domains/base.d.ts +13 -6
- package/dist/domains/base.js +31 -12
- package/dist/domains/cart.js +17 -0
- package/dist/domains/customer.js +18 -4
- package/dist/domains/order.d.ts +33 -15
- package/dist/domains/order.js +34 -20
- package/dist/domains/payment.js +16 -2
- package/dist/domains/product.d.ts +29 -40
- package/dist/domains/product.js +99 -81
- package/dist/domains/wishlist.js +4 -2
- package/dist/suite.d.ts +49 -1
- package/dist/suite.js +90 -8
- package/dist/types/adapter.d.ts +10 -7
- package/dist/types/events.d.ts +6 -2
- package/docs/future-improvements.md +116 -0
- package/package.json +15 -6
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Ecsuite — Future Improvements
|
|
2
|
+
|
|
3
|
+
A design review of the current client-side data-flow model, with gaps to consider as usage grows. Not a commitment — a backlog for discussion.
|
|
4
|
+
|
|
5
|
+
Companion to `@marianmeres/ownsuite`'s `docs/future-improvements.md`. Both suites share `BaseDomainManager`, so most structural items transfer; this document highlights what applies, what doesn't, and what is ecsuite-specific.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Ecsuite is a **fixed-domain e-commerce state manager** (cart, wishlist, order, customer, payment, product). Unlike ownsuite's generic owner-scoped CRUD, each manager has domain-specific state shape, persistence, and operations. The design is intentionally closed — a known, curated set of domains — which makes "pluggability" a non-goal.
|
|
10
|
+
|
|
11
|
+
## Where the current design aligns with best practice
|
|
12
|
+
|
|
13
|
+
- Store-per-domain FSM exposed as a Svelte store.
|
|
14
|
+
- Optimistic updates with rollback on cart/wishlist/customer.
|
|
15
|
+
- Optimistic `create` for cart items with client-assigned temp id, swapped on server response (correct pattern).
|
|
16
|
+
- `localStorage` persistence for cart/wishlist survives reload without a round-trip.
|
|
17
|
+
- ProductManager uses a TTL cache to avoid redundant fetches.
|
|
18
|
+
- Transport-agnostic adapter boundary per domain; full mock coverage for tests.
|
|
19
|
+
- Typed pubsub event bus scoped per domain (`cart:*`, `order:*`, etc.).
|
|
20
|
+
|
|
21
|
+
## Gaps inherited from the shared base
|
|
22
|
+
|
|
23
|
+
### 1. No request deduplication or in-flight cancellation
|
|
24
|
+
|
|
25
|
+
Any domain's `refresh()` can be called twice quickly (route remount, focus event) and produce parallel fetches with a last-write-wins race.
|
|
26
|
+
|
|
27
|
+
**Possible direction:** track an in-flight promise per operation key; coalesce concurrent callers.
|
|
28
|
+
|
|
29
|
+
### 2. No `AbortSignal` plumbing through adapters
|
|
30
|
+
|
|
31
|
+
Component unmount cannot cancel in-flight fetches; results land in a detached store and may overwrite fresher data.
|
|
32
|
+
|
|
33
|
+
**Possible direction:** thread `AbortSignal` through adapter method signatures; abort on supersession and disposal.
|
|
34
|
+
|
|
35
|
+
### 3. No staleness / revalidation policy (except ProductManager)
|
|
36
|
+
|
|
37
|
+
`lastSyncedAt` is tracked but unused on cart/order/customer/payment. No focus/reconnect refetch, no cross-tab sync. ProductManager's TTL is a one-off — no shared cache primitive.
|
|
38
|
+
|
|
39
|
+
**Possible direction:** lift ProductManager's TTL approach into a reusable base-level option; add optional focus/reconnect listeners behind a flag.
|
|
40
|
+
|
|
41
|
+
### 4. No per-row pending state during optimistic updates — **acute here**
|
|
42
|
+
|
|
43
|
+
More important in ecsuite than ownsuite: cart UIs routinely need "this line item is updating" spinners. Currently, modifying one cart item flips the whole cart to `syncing`, so consumers can't show a per-line spinner without their own bookkeeping.
|
|
44
|
+
|
|
45
|
+
**Possible direction:** track pending item ids in state; expose as a `Set<itemId>` alongside `items`.
|
|
46
|
+
|
|
47
|
+
### 5. Error state couples to data availability
|
|
48
|
+
|
|
49
|
+
One failed `update` puts the whole domain in `error`, even though `data` is still valid. Consumers must remember to ignore `state === "error"` when `data` exists.
|
|
50
|
+
|
|
51
|
+
**Possible direction:** decouple — keep `state` at `ready`, surface the last error as a sibling signal (`lastError`).
|
|
52
|
+
|
|
53
|
+
### 6. `initialize()` swallows errors
|
|
54
|
+
|
|
55
|
+
Silent boot failure unless you subscribe to `*:error` events.
|
|
56
|
+
|
|
57
|
+
**Possible direction:** add `suite.hasErrors()` / `suite.getErrors()` helpers.
|
|
58
|
+
|
|
59
|
+
### 7. Whole-list rollback snapshot
|
|
60
|
+
|
|
61
|
+
Less critical than in ownsuite — carts/wishlists are small. Flag for later.
|
|
62
|
+
|
|
63
|
+
## Gaps that apply to specific domains only
|
|
64
|
+
|
|
65
|
+
### 8. Query-keyed cache — OrderManager (and partly ProductManager)
|
|
66
|
+
|
|
67
|
+
Order history is naturally filtered/paginated (status, date range). A single `orders` slot can't hold multiple views concurrently. Cart/wishlist/customer/payment are session-singletons and don't need this.
|
|
68
|
+
|
|
69
|
+
**Possible direction:** upgrade OrderManager to a `queries: Map<queryKey, {...}>` shape; leave singleton managers alone.
|
|
70
|
+
|
|
71
|
+
### 9. Pagination / infinite-scroll primitives — OrderManager, ProductManager
|
|
72
|
+
|
|
73
|
+
`meta` is opaque; cursor/offset merging is consumer-implemented. Needed for order history and product listings.
|
|
74
|
+
|
|
75
|
+
**Possible direction:** first-class `loadMore()` on those managers with a pluggable merge strategy.
|
|
76
|
+
|
|
77
|
+
## Ecsuite-specific concerns (no ownsuite analogue)
|
|
78
|
+
|
|
79
|
+
### 10. localStorage persistence — multiple latent issues
|
|
80
|
+
|
|
81
|
+
- **No cross-tab sync.** Two tabs mutating the cart diverge until one wins on reload. A `storage` event listener would mirror changes.
|
|
82
|
+
- **No schema versioning / migration.** If cart item shape changes between releases, old persisted carts will either crash deserialization or silently corrupt state. A `version` field + migration hook is standard.
|
|
83
|
+
- **No encryption or redaction.** Carts can contain semi-sensitive data (product ids a user browses privately, promo codes). Probably acceptable for e-commerce norms, but worth an explicit decision.
|
|
84
|
+
- **No quota handling.** `localStorage.setItem` can throw on quota-exceeded; currently unhandled.
|
|
85
|
+
|
|
86
|
+
### 11. Temp-id → real-id swap on optimistic cart `create`
|
|
87
|
+
|
|
88
|
+
Known-hazardous pattern. Risks:
|
|
89
|
+
|
|
90
|
+
- Events fired with the temp id before swap — subscribers caching by id see a ghost.
|
|
91
|
+
- Subsequent `update`/`delete` called on a temp id while the server response is in flight — needs either queuing or id remapping.
|
|
92
|
+
- External references (URLs, analytics) captured against a temp id become stale.
|
|
93
|
+
|
|
94
|
+
**Possible direction:** document the swap contract explicitly; consider buffering mutations until the real id arrives; emit a dedicated `cart:item:id-reconciled` event so subscribers can rekey.
|
|
95
|
+
|
|
96
|
+
### 12. ProductManager TTL cache is a one-off
|
|
97
|
+
|
|
98
|
+
Useful, but not reusable. CustomerManager and OrderManager could benefit from the same primitive. See #3.
|
|
99
|
+
|
|
100
|
+
## Items that do NOT apply
|
|
101
|
+
|
|
102
|
+
- **Pluggable manager types** — ecsuite is intentionally closed (fixed 6 domains). This is a design contract, not a flaw. (Applies to ownsuite only.)
|
|
103
|
+
|
|
104
|
+
## Prioritization sketch
|
|
105
|
+
|
|
106
|
+
Rough order if tackled:
|
|
107
|
+
|
|
108
|
+
1. **#4 per-row pending on CartManager** — highest user-visible value; UIs want it now.
|
|
109
|
+
2. **#2 AbortSignal** — cheap correctness win.
|
|
110
|
+
3. **#1 dedup** — cheap, removes a class of races.
|
|
111
|
+
4. **#5 decouple error from state** — small API change, big DX win.
|
|
112
|
+
5. **#10 localStorage cross-tab sync + versioning** — bite-sized, prevents real bugs.
|
|
113
|
+
6. **#11 temp-id swap hardening** — audit existing code; document or fix concretely.
|
|
114
|
+
7. **#3 shared TTL/staleness primitive** — lift from ProductManager.
|
|
115
|
+
8. **#8 + #9 query cache & pagination for OrderManager** — defer until a concrete consumer needs it; design together.
|
|
116
|
+
9. Remaining items — opportunistic.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/ecsuite",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/mod.js",
|
|
6
6
|
"types": "dist/mod.d.ts",
|
|
@@ -10,14 +10,23 @@
|
|
|
10
10
|
"import": "./dist/mod.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"README.md",
|
|
17
|
+
"API.md",
|
|
18
|
+
"AGENTS.md",
|
|
19
|
+
"CLAUDE.md",
|
|
20
|
+
"docs"
|
|
21
|
+
],
|
|
13
22
|
"author": "Marian Meres",
|
|
14
23
|
"license": "MIT",
|
|
15
24
|
"dependencies": {
|
|
16
|
-
"@marianmeres/clog": "^3.
|
|
17
|
-
"@marianmeres/collection-types": "^1.
|
|
18
|
-
"@marianmeres/http-utils": "^2.
|
|
19
|
-
"@marianmeres/pubsub": "^
|
|
20
|
-
"@marianmeres/store": "^
|
|
25
|
+
"@marianmeres/clog": "^3.17.1",
|
|
26
|
+
"@marianmeres/collection-types": "^1.37.0",
|
|
27
|
+
"@marianmeres/http-utils": "^2.6.0",
|
|
28
|
+
"@marianmeres/pubsub": "^3.0.0",
|
|
29
|
+
"@marianmeres/store": "^3.0.1"
|
|
21
30
|
},
|
|
22
31
|
"repository": {
|
|
23
32
|
"type": "git",
|