@stacksjs/defaults 0.72.103 → 0.73.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/ai/AGENTS.md +25 -4
- package/ai/README.md +26 -4
- package/ai/skills/stacks-actions/SKILL.md +1 -1
- package/ai/skills/stacks-ai/SKILL.md +1 -1
- package/ai/skills/stacks-alias/SKILL.md +1 -1
- package/ai/skills/stacks-analytics/SKILL.md +1 -1
- package/ai/skills/stacks-api/SKILL.md +1 -1
- package/ai/skills/stacks-arrays/SKILL.md +1 -1
- package/ai/skills/stacks-auto-imports/SKILL.md +1 -1
- package/ai/skills/stacks-browse/SKILL.md +1 -1
- package/ai/skills/stacks-browser/SKILL.md +1 -1
- package/ai/skills/stacks-buddy/SKILL.md +1 -1
- package/ai/skills/stacks-build/SKILL.md +79 -5
- package/ai/skills/stacks-cache/SKILL.md +1 -1
- package/ai/skills/stacks-calendar/SKILL.md +1 -1
- package/ai/skills/stacks-chat/SKILL.md +1 -1
- package/ai/skills/stacks-cli/SKILL.md +1 -1
- package/ai/skills/stacks-cloud/SKILL.md +1 -1
- package/ai/skills/stacks-cms/SKILL.md +1 -1
- package/ai/skills/stacks-codebase-design/DEEPENING.md +79 -0
- package/ai/skills/stacks-codebase-design/DESIGN-IT-TWICE.md +72 -0
- package/ai/skills/stacks-codebase-design/SKILL.md +180 -0
- package/ai/skills/stacks-collections/SKILL.md +1 -1
- package/ai/skills/stacks-commerce/SKILL.md +141 -35
- package/ai/skills/stacks-composables/SKILL.md +1 -1
- package/ai/skills/stacks-config/SKILL.md +1 -1
- package/ai/skills/stacks-configuration/SKILL.md +1 -1
- package/ai/skills/stacks-cron/SKILL.md +1 -1
- package/ai/skills/stacks-crosswind/SKILL.md +1 -1
- package/ai/skills/stacks-database/SKILL.md +1 -1
- package/ai/skills/stacks-datetime/SKILL.md +1 -1
- package/ai/skills/stacks-dependencies/SKILL.md +1 -1
- package/ai/skills/stacks-deploy/SKILL.md +1 -1
- package/ai/skills/stacks-desktop/SKILL.md +1 -1
- package/ai/skills/stacks-development/SKILL.md +1 -1
- package/ai/skills/stacks-dns/SKILL.md +1 -1
- package/ai/skills/stacks-docs/SKILL.md +1 -1
- package/ai/skills/stacks-domain-modeling/FORMATS.md +123 -0
- package/ai/skills/stacks-domain-modeling/SKILL.md +109 -0
- package/ai/skills/stacks-enums/SKILL.md +1 -1
- package/ai/skills/stacks-error-handling/SKILL.md +1 -1
- package/ai/skills/stacks-events/SKILL.md +1 -1
- package/ai/skills/stacks-faker/SKILL.md +1 -1
- package/ai/skills/stacks-flow/PHASE-BOUNDARIES.md +91 -0
- package/ai/skills/stacks-flow/SKILL.md +117 -0
- package/ai/skills/stacks-git/SKILL.md +37 -9
- package/ai/skills/stacks-grilling/SKILL.md +85 -0
- package/ai/skills/stacks-guard/SKILL.md +86 -11
- package/ai/skills/stacks-guard/scripts/block-destructive.sh +67 -0
- package/ai/skills/stacks-handoff/SKILL.md +70 -0
- package/ai/skills/stacks-health/SKILL.md +1 -1
- package/ai/skills/stacks-http/SKILL.md +1 -1
- package/ai/skills/stacks-i18n/SKILL.md +1 -1
- package/ai/skills/stacks-investigate/SKILL.md +234 -106
- package/ai/skills/stacks-investigate/scripts/hitl-loop.template.sh +47 -0
- package/ai/skills/stacks-jobs/SKILL.md +1 -1
- package/ai/skills/stacks-listeners/SKILL.md +1 -1
- package/ai/skills/stacks-logging/SKILL.md +1 -1
- package/ai/skills/stacks-mail/SKILL.md +1 -1
- package/ai/skills/stacks-middleware/SKILL.md +1 -1
- package/ai/skills/stacks-migrations/SKILL.md +1 -1
- package/ai/skills/stacks-models/SKILL.md +1 -1
- package/ai/skills/stacks-new-feature/SKILL.md +65 -5
- package/ai/skills/stacks-notifications/SKILL.md +1 -1
- package/ai/skills/stacks-objects/SKILL.md +1 -1
- package/ai/skills/stacks-office-hours/SKILL.md +16 -2
- package/ai/skills/stacks-orm/SKILL.md +1 -1
- package/ai/skills/stacks-path/SKILL.md +1 -1
- package/ai/skills/stacks-payments/SKILL.md +35 -1
- package/ai/skills/stacks-plan-review/SKILL.md +27 -6
- package/ai/skills/stacks-plugins/SKILL.md +1 -1
- package/ai/skills/stacks-prototype/LOGIC.md +103 -0
- package/ai/skills/stacks-prototype/SKILL.md +66 -0
- package/ai/skills/stacks-prototype/UI.md +112 -0
- package/ai/skills/stacks-push/SKILL.md +1 -1
- package/ai/skills/stacks-query-builder/SKILL.md +1 -1
- package/ai/skills/stacks-queue/SKILL.md +1 -1
- package/ai/skills/stacks-realtime/SKILL.md +1 -1
- package/ai/skills/stacks-registry/SKILL.md +1 -1
- package/ai/skills/stacks-repl/SKILL.md +1 -1
- package/ai/skills/stacks-retro/SKILL.md +121 -75
- package/ai/skills/stacks-review/SKILL.md +182 -74
- package/ai/skills/stacks-router/SKILL.md +1 -1
- package/ai/skills/stacks-routes/SKILL.md +1 -1
- package/ai/skills/stacks-scaffolding/SKILL.md +1 -1
- package/ai/skills/stacks-scheduler/SKILL.md +1 -1
- package/ai/skills/stacks-search-engine/SKILL.md +1 -1
- package/ai/skills/stacks-security/SKILL.md +1 -1
- package/ai/skills/stacks-security-audit/SKILL.md +1 -1
- package/ai/skills/stacks-server/SKILL.md +1 -1
- package/ai/skills/stacks-shell/SKILL.md +1 -1
- package/ai/skills/stacks-slug/SKILL.md +1 -1
- package/ai/skills/stacks-sms/SKILL.md +1 -1
- package/ai/skills/stacks-socials/SKILL.md +1 -1
- package/ai/skills/stacks-storage/SKILL.md +1 -1
- package/ai/skills/stacks-strings/SKILL.md +1 -1
- package/ai/skills/stacks-stx/SKILL.md +1 -1
- package/ai/skills/stacks-tdd/EXAMPLES.md +136 -0
- package/ai/skills/stacks-tdd/SKILL.md +125 -0
- package/ai/skills/stacks-testing/SKILL.md +13 -3
- package/ai/skills/stacks-tunnel/SKILL.md +1 -1
- package/ai/skills/stacks-types/SKILL.md +1 -1
- package/ai/skills/stacks-ui/SKILL.md +1 -1
- package/ai/skills/stacks-utils/SKILL.md +1 -1
- package/ai/skills/stacks-validation/SKILL.md +1 -1
- package/ai/skills/stacks-whois/SKILL.md +1 -1
- package/ai/skills/stacks-wizard/SKILL.md +127 -0
- package/ai/skills/stacks-wizard/scripts/template.sh +208 -0
- package/ai/skills/stacks-writing-for-agents/MECHANICS.md +125 -0
- package/ai/skills/stacks-writing-for-agents/SKILL.md +218 -0
- package/app/Actions/Auth/GenerateTwoFactorSecretAction.ts +12 -2
- package/app/Actions/Commerce/Shipping/{DriverDestroyAction.ts → CourierDestroyAction.ts} +6 -6
- package/app/Actions/Commerce/Shipping/{DriverIndexAction.ts → CourierIndexAction.ts} +3 -3
- package/app/Actions/Commerce/Shipping/CourierPingStoreAction.ts +61 -0
- package/app/Actions/Commerce/Shipping/{DriverShowAction.ts → CourierShowAction.ts} +5 -5
- package/app/Actions/Commerce/Shipping/{DriverStoreAction.ts → CourierStoreAction.ts} +4 -4
- package/app/Actions/Commerce/Shipping/{DriverUpdateAction.ts → CourierUpdateAction.ts} +6 -6
- package/app/Actions/Commerce/Shipping/DeliveryRouteStartAction.ts +38 -0
- package/app/Actions/Commerce/Shipping/DeliveryStopCompleteAction.ts +40 -0
- package/app/Actions/Commerce/Shipping/DeliveryStopFailAction.ts +44 -0
- package/app/Actions/Commerce/Shipping/DeliveryStopStartAction.ts +39 -0
- package/app/Actions/Commerce/Shipping/courier-session.ts +75 -0
- package/app/Actions/Commerce/commerce-action.test.ts +5 -5
- package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +5 -5
- package/app/Actions/Dashboard/Commerce/CourierIndexAction.ts +24 -0
- package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +7 -7
- package/app/Actions/Dashboard/Commerce/commerce-delivery.test.ts +11 -11
- package/app/Actions/Dashboard/Commerce/commerce-delivery.ts +29 -29
- package/app/Actions/Dashboard/Commerce/{driver-records.test.ts → courier-records.test.ts} +9 -9
- package/app/Actions/Dashboard/Commerce/{driver-records.ts → courier-records.ts} +14 -14
- package/app/Actions/Dashboard/Commerce/delivery-route-records.test.ts +13 -13
- package/app/Actions/Dashboard/Commerce/delivery-route-records.ts +25 -25
- package/app/Models/User.ts +1 -1
- package/app/Models/commerce/{Driver.ts → Courier.ts} +7 -7
- package/app/Models/commerce/{DriverPing.ts → CourierPing.ts} +7 -7
- package/app/Models/commerce/DeliveryRoute.ts +6 -6
- package/app/Models/commerce/DeliveryStop.ts +31 -8
- package/bootstrap.ts +7 -0
- package/functions/commerce/shippings/couriers.ts +19 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +4 -3
- package/resources/components/Dashboard/Commerce/Delivery/{DriverDeleteDialog.stx → CourierDeleteDialog.stx} +5 -5
- package/resources/components/Dashboard/Commerce/Delivery/{DriverDialog.stx → CourierDialog.stx} +10 -10
- package/resources/components/Dashboard/Commerce/Delivery/{DriversDashboard.stx → CouriersDashboard.stx} +43 -43
- package/resources/components/Dashboard/Commerce/Delivery/{DriversTable.stx → CouriersTable.stx} +21 -21
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryOverviewDashboard.stx +18 -18
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDeleteDialog.stx +2 -2
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRouteDialog.stx +22 -22
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesDashboard.stx +24 -24
- package/resources/components/Dashboard/Commerce/Delivery/DeliveryRoutesTable.stx +8 -8
- package/resources/components/Dashboard/Commerce/Delivery/TabNavigation.stx +1 -1
- package/resources/functions/dashboard/data.ts +1 -1
- package/resources/functions/dashboard/sidebar.ts +2 -2
- package/routes/dashboard-api.ts +6 -6
- package/routes/dashboard.ts +7 -7
- package/routes/delivery.ts +24 -0
- package/types/defaults.ts +3 -3
- package/views/dashboard/.discovered-models.json +18 -18
- package/views/dashboard/AUDIT.md +1 -1
- package/views/dashboard/commerce/delivery/{drivers.stx → couriers.stx} +2 -2
- package/views/dashboard/composables/useChart.ts +16 -2
- package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +0 -24
- package/functions/commerce/shippings/drivers.ts +0 -19
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stacks-codebase-design
|
|
3
|
+
description: Use when designing or restructuring code in a Stacks project - shaping an action or package interface, deciding where a seam goes, choosing between a trait and a helper, making code testable or navigable, or when another skill needs the deep-module vocabulary (module, interface, depth, seam, adapter, leverage, locality).
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
|
+
allowed-tools: Read Edit Write Bash Grep Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Codebase design
|
|
10
|
+
|
|
11
|
+
Design **deep modules**: a lot of behaviour behind a small interface, placed at a
|
|
12
|
+
clean seam, testable through that interface. Use this language and these
|
|
13
|
+
principles wherever Stacks code is being designed or restructured. The aim is
|
|
14
|
+
leverage for callers, locality for maintainers, and testability for everyone.
|
|
15
|
+
|
|
16
|
+
Stacks is already built this way, which is why the vocabulary is worth sharing:
|
|
17
|
+
`defineModel()` puts a schema, validation, factories, relationships and a REST
|
|
18
|
+
surface behind one call. The driver packages (`cache`, `queue`, `storage`,
|
|
19
|
+
`email`, `ai`, `chat`, `search-engine`, `dns`) are ports with two or more
|
|
20
|
+
adapters each. Use the words below rather than inventing new ones per package.
|
|
21
|
+
|
|
22
|
+
Credit: the deep-module model here is adapted from Matt Pocock's
|
|
23
|
+
`codebase-design` skill (MIT), <https://github.com/mattpocock/skills>, itself
|
|
24
|
+
building on Ousterhout and Feathers.
|
|
25
|
+
|
|
26
|
+
## Glossary
|
|
27
|
+
|
|
28
|
+
Use these terms exactly. Do not substitute "component", "service", "API" or
|
|
29
|
+
"boundary". Consistent language is the whole point.
|
|
30
|
+
|
|
31
|
+
**Module**: anything with an interface and an implementation. Deliberately
|
|
32
|
+
scale-agnostic: a function, an action, a model, a `@stacksjs/*` package, a
|
|
33
|
+
tier-spanning slice. *Avoid*: unit, component, service.
|
|
34
|
+
|
|
35
|
+
**Interface**: everything a caller must know to use the module correctly. The
|
|
36
|
+
type signature, but also invariants, ordering constraints, error modes, required
|
|
37
|
+
config, and performance characteristics. For a Stacks action the interface
|
|
38
|
+
includes its route, its middleware and its request shape, not just its `handle`
|
|
39
|
+
signature. *Avoid*: API, signature (too narrow, they name only the type surface).
|
|
40
|
+
|
|
41
|
+
**Implementation**: what is inside a module. Distinct from **adapter**: a thing
|
|
42
|
+
can be a small adapter over a large implementation (the S3 storage driver) or a
|
|
43
|
+
large adapter over a small one (an in-memory fake). Reach for "adapter" when the
|
|
44
|
+
seam is the topic, "implementation" otherwise.
|
|
45
|
+
|
|
46
|
+
**Depth**: leverage at the interface. How much behaviour a caller or a test can
|
|
47
|
+
exercise per unit of interface they have to learn. A module is **deep** when a
|
|
48
|
+
lot of behaviour sits behind a small interface, **shallow** when the interface is
|
|
49
|
+
nearly as complex as the implementation.
|
|
50
|
+
|
|
51
|
+
**Seam** (Michael Feathers): a place where you can alter behaviour without
|
|
52
|
+
editing in that place. The *location* at which a module's interface lives. Where
|
|
53
|
+
to put the seam is its own design decision, distinct from what goes behind it. In
|
|
54
|
+
Stacks the `app/` override model is a seam the framework hands you for free:
|
|
55
|
+
`app/Actions/Cms/PostIndexAction.ts` replaces the default without editing it.
|
|
56
|
+
*Avoid*: boundary (overloaded with DDD's bounded context).
|
|
57
|
+
|
|
58
|
+
**Adapter**: a concrete thing that satisfies an interface at a seam. Describes
|
|
59
|
+
*role* (which slot it fills), not substance (what is inside). `config/cache.ts`
|
|
60
|
+
picking `memory` or `redis` is adapter selection.
|
|
61
|
+
|
|
62
|
+
**Leverage**: what callers get from depth. More capability per unit of interface
|
|
63
|
+
learned. One implementation pays back across N call sites and M tests.
|
|
64
|
+
|
|
65
|
+
**Locality**: what maintainers get from depth. Change, bugs, knowledge and
|
|
66
|
+
verification concentrate in one place rather than spreading across callers. Fix
|
|
67
|
+
once, fixed everywhere.
|
|
68
|
+
|
|
69
|
+
## Deep versus shallow
|
|
70
|
+
|
|
71
|
+
**Deep** = small interface, lots of implementation:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
┌─────────────────────┐
|
|
75
|
+
│ Small interface │ ← few entry points, simple params
|
|
76
|
+
├─────────────────────┤
|
|
77
|
+
│ │
|
|
78
|
+
│ Deep implementation│ ← complexity hidden
|
|
79
|
+
│ │
|
|
80
|
+
└─────────────────────┘
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Shallow** = large interface, little implementation. Avoid:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
┌─────────────────────────────────┐
|
|
87
|
+
│ Large interface │ ← many methods, complex params
|
|
88
|
+
├─────────────────────────────────┤
|
|
89
|
+
│ Thin implementation │ ← mostly passes through
|
|
90
|
+
└─────────────────────────────────┘
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
When designing an interface, ask: can I reduce the number of entry points? Can I
|
|
94
|
+
simplify the params? Can I hide more complexity inside?
|
|
95
|
+
|
|
96
|
+
The `useApi` trait is the canonical deep interface in this framework: one config
|
|
97
|
+
object, and the model gains five actions, five routes, an OpenAPI entry and a
|
|
98
|
+
dashboard view. `useSearch`, `useAuth` and `useSoftDeletes` are the same trade.
|
|
99
|
+
When you find yourself writing the fifth near-identical action, the question is
|
|
100
|
+
whether a trait wants to be born.
|
|
101
|
+
|
|
102
|
+
## Principles
|
|
103
|
+
|
|
104
|
+
- **Depth is a property of the interface, not the implementation.** A deep module
|
|
105
|
+
can be internally composed of small, swappable parts. They just are not part of
|
|
106
|
+
the interface. A module can have **internal seams** (private, used by its own
|
|
107
|
+
tests) as well as the **external seam** at its interface.
|
|
108
|
+
- **The deletion test.** Imagine deleting the module. If complexity vanishes, it
|
|
109
|
+
was a pass-through. If complexity reappears across N callers, it was earning
|
|
110
|
+
its keep.
|
|
111
|
+
- **The interface is the test surface.** Callers and tests cross the same seam.
|
|
112
|
+
If you want to test *past* the interface, the module is probably the wrong
|
|
113
|
+
shape.
|
|
114
|
+
- **One adapter means a hypothetical seam. Two adapters means a real one.** Do
|
|
115
|
+
not introduce a seam unless something actually varies across it. Production
|
|
116
|
+
plus test counts as two.
|
|
117
|
+
|
|
118
|
+
## Designing for testability
|
|
119
|
+
|
|
120
|
+
1. **Accept dependencies, do not create them.**
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// testable
|
|
124
|
+
export async function processOrder(order: Order, gateway: PaymentGateway) {}
|
|
125
|
+
|
|
126
|
+
// hard to test
|
|
127
|
+
export async function processOrder(order: Order) {
|
|
128
|
+
const gateway = new StripeGateway()
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
2. **Return results, do not produce side effects.**
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// testable
|
|
136
|
+
function calculateDiscount(cart: Cart): Discount {}
|
|
137
|
+
|
|
138
|
+
// hard to test
|
|
139
|
+
function applyDiscount(cart: Cart): void { cart.total -= discount }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
3. **Small surface area.** Fewer entry points means fewer tests. Fewer params
|
|
143
|
+
means simpler setup.
|
|
144
|
+
|
|
145
|
+
In a Stacks app the third dependency is almost always the database, and it does
|
|
146
|
+
not need injecting: `refreshDatabase()` plus model factories give you a real one
|
|
147
|
+
per suite. See `stacks-tdd` for where that line sits.
|
|
148
|
+
|
|
149
|
+
## Relationships
|
|
150
|
+
|
|
151
|
+
- A **module** has exactly one **interface**, the surface it presents to callers
|
|
152
|
+
and tests.
|
|
153
|
+
- **Depth** is a property of a **module**, measured against its **interface**.
|
|
154
|
+
- A **seam** is where a **module**'s **interface** lives.
|
|
155
|
+
- An **adapter** sits at a **seam** and satisfies the **interface**.
|
|
156
|
+
- **Depth** produces **leverage** for callers and **locality** for maintainers.
|
|
157
|
+
|
|
158
|
+
## Rejected framings
|
|
159
|
+
|
|
160
|
+
- **Depth as a ratio of implementation lines to interface lines** (Ousterhout):
|
|
161
|
+
rewards padding the implementation. Use depth-as-leverage instead.
|
|
162
|
+
- **"Interface" as the TypeScript `interface` keyword or a class's public
|
|
163
|
+
methods**: too narrow. Interface here includes every fact a caller must know.
|
|
164
|
+
- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or
|
|
165
|
+
**interface**.
|
|
166
|
+
|
|
167
|
+
## Going deeper
|
|
168
|
+
|
|
169
|
+
- **Deepening a cluster given its dependencies**: [DEEPENING.md](DEEPENING.md)
|
|
170
|
+
covers the dependency categories, seam discipline, and replace-do-not-layer
|
|
171
|
+
testing.
|
|
172
|
+
- **Exploring alternative interfaces**: [DESIGN-IT-TWICE.md](DESIGN-IT-TWICE.md)
|
|
173
|
+
runs parallel sub-agents to design one interface several radically different
|
|
174
|
+
ways, then compares on depth, locality and seam placement.
|
|
175
|
+
|
|
176
|
+
## Downstream
|
|
177
|
+
|
|
178
|
+
> Reach for `stacks-tdd` to write the tests at the seam you chose, and
|
|
179
|
+
> `stacks-domain-modeling` when the module needs a name the project does not
|
|
180
|
+
> have yet.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-collections
|
|
3
|
-
description: Use when working with collection data structures in Stacks
|
|
3
|
+
description: Use when working with collection data structures in Stacks - chaining array operations, Laravel-style collection methods, mapping, filtering, reducing, or grouping. Covers @stacksjs/collections which wraps ts-collect.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-commerce
|
|
3
|
-
description: Use when building e-commerce features in Stacks
|
|
3
|
+
description: Use when building e-commerce features in Stacks - the commerce namespace with 15 sub-modules (products, carts, orders, customers, coupons, payments, gift cards, auctions, shipping, tax, waitlists, restaurant, devices, receipts, errors), 20+ commerce models, checkout and redemption logic, or the commerce configuration. Covers @stacksjs/commerce.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -8,7 +8,7 @@ allowed-tools: Read Edit Write Bash Grep Glob
|
|
|
8
8
|
|
|
9
9
|
# Stacks Commerce
|
|
10
10
|
|
|
11
|
-
Comprehensive e-commerce module with
|
|
11
|
+
Comprehensive e-commerce module with 15 sub-modules and 20+ models.
|
|
12
12
|
|
|
13
13
|
## Key Paths
|
|
14
14
|
- Core package: `storage/framework/core/commerce/src/`
|
|
@@ -20,30 +20,37 @@ Comprehensive e-commerce module with 13 sub-modules and 20+ models.
|
|
|
20
20
|
```typescript
|
|
21
21
|
import { commerce } from '@stacksjs/commerce'
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
commerce.products //
|
|
25
|
-
commerce.
|
|
26
|
-
commerce.
|
|
27
|
-
commerce.
|
|
28
|
-
commerce.
|
|
29
|
-
commerce.
|
|
30
|
-
commerce.
|
|
31
|
-
commerce.
|
|
32
|
-
commerce.shippings //
|
|
33
|
-
commerce.tax // Tax
|
|
34
|
-
commerce.waitlists //
|
|
23
|
+
// 15 sub-modules
|
|
24
|
+
commerce.products // Products, variants, units, manufacturers, reviews, categories
|
|
25
|
+
commerce.carts // The pre-checkout basket
|
|
26
|
+
commerce.orders // Orders, order items, export, checkout guards
|
|
27
|
+
commerce.customers // Customer records
|
|
28
|
+
commerce.coupons // Coupons, including atomic redemption
|
|
29
|
+
commerce.payments // Payment records, refunds
|
|
30
|
+
commerce.giftCards // Gift cards, balance, redemption and reload
|
|
31
|
+
commerce.auctions // Benefit auctions: lots, bids, proxy bidding, pledges
|
|
32
|
+
commerce.shippings // Methods, rates, zones, routes, couriers, live tracking
|
|
33
|
+
commerce.tax // Tax rates and breakdown
|
|
34
|
+
commerce.waitlists // Waitlists
|
|
35
|
+
commerce.restaurant // Restaurant features (lives at waitlists/restaurant)
|
|
35
36
|
commerce.devices // Print device management
|
|
36
|
-
commerce.receipts // Receipt
|
|
37
|
+
commerce.receipts // Receipt records
|
|
38
|
+
commerce.errors // Error tracking
|
|
37
39
|
```
|
|
38
40
|
|
|
41
|
+
`restaurant` is re-exported from `waitlists/restaurant` rather than having a
|
|
42
|
+
directory of its own, which is why the source tree shows one fewer directory
|
|
43
|
+
than the namespace has keys. `tests/commerce.test.ts` asserts the exact key set,
|
|
44
|
+
so adding a sub-module means updating that count.
|
|
45
|
+
|
|
39
46
|
## Sub-Module Operations
|
|
40
47
|
|
|
41
48
|
Each sub-module typically provides:
|
|
42
|
-
- `
|
|
43
|
-
- `
|
|
44
|
-
- `store(data)`
|
|
45
|
-
- `update(id, data)`
|
|
46
|
-
- `destroy(id)`
|
|
49
|
+
- `fetchAll()` - list all
|
|
50
|
+
- `fetchById(id)` - get one
|
|
51
|
+
- `store(data)` - create, plus `bulkStore`
|
|
52
|
+
- `update(id, data)` - update, plus `bulkUpdate`
|
|
53
|
+
- `destroy(id)` - delete, plus `bulkDestroy`
|
|
47
54
|
|
|
48
55
|
### Products Sub-Module
|
|
49
56
|
- Products: items, variants, units
|
|
@@ -62,7 +69,7 @@ Each sub-module typically provides:
|
|
|
62
69
|
- Shipping rates (weight-based)
|
|
63
70
|
- Shipping zones
|
|
64
71
|
- Delivery routes and their stops
|
|
65
|
-
-
|
|
72
|
+
- Couriers
|
|
66
73
|
- Digital deliveries
|
|
67
74
|
- License keys
|
|
68
75
|
- **Live tracking** (`commerce.shippings.tracking`) - see below
|
|
@@ -85,16 +92,77 @@ Each sub-module typically provides:
|
|
|
85
92
|
| Manufacturer | manufacturer info | hasMany: Product |
|
|
86
93
|
| Review | rating(1-5), content, isVerifiedPurchase, helpfulVotes | belongsTo: Product, Customer |
|
|
87
94
|
| ShippingRate | weightFrom, weightTo, rate | belongsTo: ShippingMethod, ShippingZone |
|
|
88
|
-
| DeliveryRoute | stops, totalDistance, status(planned/active/completed), startedAt | belongsTo:
|
|
95
|
+
| DeliveryRoute | stops, totalDistance, status(planned/active/completed), startedAt | belongsTo: Courier; hasMany: DeliveryStop, CourierPing |
|
|
89
96
|
| DeliveryStop | sequence, status, address, latitude, longitude, etaAt, arrivedAt | belongsTo: DeliveryRoute, Order |
|
|
90
|
-
|
|
|
91
|
-
|
|
|
97
|
+
| Courier | name, phone, vehicleNumber, status, latitude, longitude, heading, lastPingAt | hasMany: DeliveryRoute, CourierPing |
|
|
98
|
+
| CourierPing | latitude, longitude, heading, speed, accuracy, recordedAt | belongsTo: Courier, DeliveryRoute |
|
|
92
99
|
| TaxRate | name, rate(0-100), type(VAT/GST/Sales Tax) | |
|
|
93
100
|
| LicenseKey | key(XXXX-XXXX-XXXX-XXXX-XXXX), template, status | belongsTo: Customer, Product, Order |
|
|
94
101
|
| DigitalDelivery | downloadLimit, expiryDays, automaticDelivery | |
|
|
95
102
|
| WaitlistProduct | product waitlist tracking | |
|
|
96
103
|
| Receipt | receipt records | |
|
|
97
104
|
|
|
105
|
+
## Money paths
|
|
106
|
+
|
|
107
|
+
Three operations move money, and all three are written as a **single conditional
|
|
108
|
+
UPDATE** rather than read-check-write. Concurrency here is not theoretical: two
|
|
109
|
+
parallel requests against a read-then-write redemption both see the pre-state
|
|
110
|
+
and both succeed, which is a coupon redeemed past its limit or a gift card spent
|
|
111
|
+
twice.
|
|
112
|
+
|
|
113
|
+
### Redeeming a coupon
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const result = await commerce.coupons.redeem(couponId)
|
|
117
|
+
|
|
118
|
+
if (!result.ok) {
|
|
119
|
+
// 'not-found' | 'inactive' | 'expired' | 'limit-reached'
|
|
120
|
+
throw new HttpError(400, `Coupon cannot be redeemed: ${result.reason}`)
|
|
121
|
+
}
|
|
122
|
+
// result.coupon reflects the post-redemption state.
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`redeem` bumps `usage_count` and enforces `max_uses`, `is_active` and the
|
|
126
|
+
start/end dates in the WHERE clause, so the database decides the race. A
|
|
127
|
+
`max_uses` of `NULL` means unlimited. Do not fetch, check and then call
|
|
128
|
+
`update()` to increment: that is the exact pattern this replaced.
|
|
129
|
+
|
|
130
|
+
### Spending or reloading a gift card
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
await commerce.giftCards.updateBalance(cardId, -25) // redeem 25
|
|
134
|
+
await commerce.giftCards.updateBalance(cardId, 50) // reload 50
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
One entry point for both directions, and they are deliberately asymmetric:
|
|
138
|
+
|
|
139
|
+
| | Redemption (`amount < 0`) | Reload (`amount > 0`) |
|
|
140
|
+
|---|---|---|
|
|
141
|
+
| Allowed status | `ACTIVE` | `ACTIVE`, or `USED` when the card is `isReloadable` |
|
|
142
|
+
| `lastUsedDate` | stamped | left alone |
|
|
143
|
+
| Balance floor | cannot go below 0 | n/a |
|
|
144
|
+
| Expiry | refused past `expiryDate` | refused past `expiryDate` |
|
|
145
|
+
|
|
146
|
+
The balance lands on 0 and the status flips to `USED` in the same statement. A
|
|
147
|
+
reloadable card can be revived from `USED`; a non-reloadable one cannot, and
|
|
148
|
+
throws `Gift card is not reloadable`. `deactivate(id)` is the terminal state and
|
|
149
|
+
reports whether a row actually changed.
|
|
150
|
+
|
|
151
|
+
### Recording a refund
|
|
152
|
+
|
|
153
|
+
`commerce.payments.recordRefund(id, amount)` takes **integer minor units** and
|
|
154
|
+
enforces `refund_amount + amount <= amount` in the WHERE clause, so operators
|
|
155
|
+
cannot over-refund a payment by racing each other. It flips the status to
|
|
156
|
+
`refunded` or `partiallyRefunded` depending on where the total lands.
|
|
157
|
+
|
|
158
|
+
## Carts
|
|
159
|
+
|
|
160
|
+
`commerce.carts` is the pre-checkout basket, with the same CRUD shape as every
|
|
161
|
+
other sub-module plus bulk variants. `commerce.orders` owns what happens after
|
|
162
|
+
checkout, and `orders/guards.ts` holds the pre-flight checks that run between
|
|
163
|
+
the two, including `cleanupAbandonedCarts({ olderThanDays, limit })` for the
|
|
164
|
+
sweeper you schedule daily.
|
|
165
|
+
|
|
98
166
|
## Live Delivery Tracking
|
|
99
167
|
|
|
100
168
|
`commerce.shippings.tracking` is the moving part of shipping: position ingest,
|
|
@@ -116,22 +184,22 @@ const stop = await tracking.assignStop({
|
|
|
116
184
|
await tracking.startRoute(route.id)
|
|
117
185
|
await tracking.startStop(stop.id) // order -> OUT_FOR_DELIVERY
|
|
118
186
|
|
|
119
|
-
// One call per position fix from the
|
|
120
|
-
await tracking.
|
|
121
|
-
|
|
187
|
+
// One call per position fix from the courier's device.
|
|
188
|
+
await tracking.recordCourierPing({
|
|
189
|
+
courierId, latitude, longitude, speed, accuracy,
|
|
122
190
|
})
|
|
123
191
|
|
|
124
192
|
await tracking.completeStop(stop.id) // order -> DELIVERED, route closes itself
|
|
125
193
|
```
|
|
126
194
|
|
|
127
|
-
### What `
|
|
195
|
+
### What `recordCourierPing` does
|
|
128
196
|
|
|
129
197
|
One entry point, so a tracking page never shows a position its ETA disagrees
|
|
130
|
-
with. Per fix it: appends to `
|
|
198
|
+
with. Per fix it: appends to `courier_pings`, updates the courier's denormalised
|
|
131
199
|
present position, recomputes the served stop's ETA, broadcasts the position,
|
|
132
200
|
and latches `delivery:nearby` / `delivery:arrived` so each fires exactly once.
|
|
133
201
|
|
|
134
|
-
A fix reporting worse than 250m accuracy is stored but does not move the
|
|
202
|
+
A fix reporting worse than 250m accuracy is stored but does not move the courier
|
|
135
203
|
or trip a threshold.
|
|
136
204
|
|
|
137
205
|
### Two fan-outs, on purpose
|
|
@@ -159,7 +227,7 @@ back to `SHIPPED` when a drop fails and the parcel returns to the depot.
|
|
|
159
227
|
`distanceInMeters`, `bearingInDegrees`, `estimateSecondsRemaining`, `isWithin`
|
|
160
228
|
and `hasCoordinates` are exported for building dispatch views. The ETA pads
|
|
161
229
|
straight-line distance by a detour factor and returns `null` for a stationary
|
|
162
|
-
|
|
230
|
+
courier rather than `Infinity`.
|
|
163
231
|
|
|
164
232
|
## Integration with Payments
|
|
165
233
|
Commerce works with `@stacksjs/payments` for Stripe integration:
|
|
@@ -172,13 +240,51 @@ await Payment.charge(customer, order.totalAmount, paymentMethodId)
|
|
|
172
240
|
All commerce models have dashboard views at `/dashboard/commerce/*`.
|
|
173
241
|
|
|
174
242
|
## Gotchas
|
|
175
|
-
|
|
176
|
-
|
|
243
|
+
|
|
244
|
+
### Writing raw SQL here
|
|
245
|
+
|
|
246
|
+
Two dialect traps have each shipped a broken money path, so both are now guarded
|
|
247
|
+
by `src/tests/sql-dialect-portability.test.ts`:
|
|
248
|
+
|
|
249
|
+
- **Placeholders.** Postgres numbers them (`$1`); a literal `?` is a syntax
|
|
250
|
+
error. Render them with `sqlHelpers(env.DB_CONNECTION || 'sqlite').param(n)`.
|
|
251
|
+
- **Booleans.** A `schema.boolean()` attribute becomes a real `BOOLEAN` on
|
|
252
|
+
Postgres, so `is_active = 1` is `operator does not exist: boolean = integer`.
|
|
253
|
+
Use `sqlHelpers(...).boolTrue` / `.boolFalse`.
|
|
254
|
+
|
|
255
|
+
Both pass on SQLite, which is what the tests run against, so neither shows up
|
|
256
|
+
locally. That is the whole reason the source-level test exists.
|
|
257
|
+
|
|
258
|
+
### Reading back a row you just inserted
|
|
259
|
+
|
|
260
|
+
Use `insertedId(result)` from `utils/inserted-id`. Couriers disagree: SQLite
|
|
261
|
+
reports `lastInsertRowid`, MySQL reports `insertId`, and Postgres reports
|
|
262
|
+
neither without a `RETURNING` clause (fall back to the `uuid` the row was
|
|
263
|
+
written with).
|
|
264
|
+
|
|
265
|
+
**Never read a row count as an id.** `numInsertedOrUpdatedRows` says how many
|
|
266
|
+
rows changed, not which one, so a successful single-row insert reports `1` and
|
|
267
|
+
the caller fetches row 1 of the table instead of the new record. `mutationCount`
|
|
268
|
+
is the helper for when the count is genuinely what you want.
|
|
269
|
+
|
|
270
|
+
### Everything else
|
|
271
|
+
|
|
272
|
+
- Commerce models are auto-generated - edit definitions, not generated files
|
|
273
|
+
- Run `buddy generate:migrations` after changing a commerce model, and read the
|
|
274
|
+
SQL before applying it
|
|
177
275
|
- Order `observe: true` emits events on create/update/delete
|
|
178
276
|
- Products have JSON fields for allergens and nutritionalInfo
|
|
179
|
-
- Cart expiry is tracked via `expiresAt`
|
|
277
|
+
- Cart expiry is tracked via `expiresAt`; `cleanupAbandonedCarts` is the sweeper
|
|
180
278
|
- Coupon types: `fixed_amount` or `percentage`
|
|
181
279
|
- Gift card codes are unique and auto-generated
|
|
182
280
|
- License keys follow XXXX-XXXX-XXXX-XXXX-XXXX format
|
|
183
281
|
- Product dashboard is highlighted (`dashboard: { highlight: true }`)
|
|
184
|
-
- Default seeder counts: Product(10), Order(20), Review(50), Payment(50)
|
|
282
|
+
- Default seeder counts: Product(10), Order(20), Review(50), Payment(50),
|
|
283
|
+
GiftCard(20), Customer(20), Coupon(15)
|
|
284
|
+
- `fetchById` in most sub-modules returns the raw row, so columns arrive
|
|
285
|
+
snake_cased even though the declared type is camelCase
|
|
286
|
+
|
|
287
|
+
## Downstream
|
|
288
|
+
|
|
289
|
+
> Touching a money path? `/stacks-tdd` for the seam to test it at, and
|
|
290
|
+
> `/stacks-review` before it merges. `/stacks-payments` covers the Stripe side.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-composables
|
|
3
|
-
description: Use when creating or using reactive composables in STX templates
|
|
3
|
+
description: Use when creating or using reactive composables in STX templates - 90+ composables for state management, DOM interaction, sensors, animation, browser APIs, async operations, or the complete list of auto-imported composables. Covers @stacksjs/composables.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-config
|
|
3
|
-
description: Use when working with Stacks configuration
|
|
3
|
+
description: Use when working with Stacks configuration - the 44 config files, config helper functions, default values, environment-specific overrides, or the defineApp/defineDatabase/etc builder functions. Covers @stacksjs/config and the config/ directory.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-configuration
|
|
3
|
-
description: Use when setting up or modifying Stacks project-level configuration
|
|
3
|
+
description: Use when setting up or modifying Stacks project-level configuration - bunfig.toml preload order, the tsconfig chain and TypeScript 7 / tsgo type checking, workspace configuration, .env setup, package.json scripts, system requirements (Bun >= 1.3.0, SQLite >= 3.47.2), or the project bootstrap process. For individual feature configs (database, email, auth, etc.), see the specific package skills instead.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-cron
|
|
3
|
-
description: Use when working with cron expressions in a Stacks application
|
|
3
|
+
description: Use when working with cron expressions in a Stacks application - parsing cron syntax, registering OS-level cron jobs, or low-level scheduling. Covers @stacksjs/cron. For higher-level scheduling, see stacks-scheduler.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-crosswind
|
|
3
|
-
description: Use when styling components in a Stacks application
|
|
3
|
+
description: Use when styling components in a Stacks application - utility-first CSS classes, theming, responsive design, variants, custom rules, or CSS generation. Crosswind is the CSS utility engine powering Stacks' Crosswind config.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-database
|
|
3
|
-
description: Use when working with databases in a Stacks application
|
|
3
|
+
description: Use when working with databases in a Stacks application - configuring connections, running queries, migrations, seeding, SQL helpers, or using SQLite/MySQL/PostgreSQL/DynamoDB. Covers @stacksjs/database, bun-query-builder, config/database.ts, and the database/ migrations directory.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript, SQLite >= 3.47.2
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-datetime
|
|
3
|
-
description: Use when working with dates and times in Stacks
|
|
3
|
+
description: Use when working with dates and times in Stacks - the DateTime class with Carbon-like API (add/sub, comparison, formatting, start/end of day/month/year), date parsing, format tokens, or timezone handling. Covers @stacksjs/datetime.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-dependencies
|
|
3
|
-
description: Use when managing dependencies in a Stacks project
|
|
3
|
+
description: Use when managing dependencies in a Stacks project - system dependencies via Pantry, Bun workspaces, buddy-bot updates, better-dx tooling, or dependency configuration. Covers config/deps.ts, Pantry, and workspace management.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-deploy
|
|
3
|
-
description: Use when deploying a Stacks application
|
|
3
|
+
description: Use when deploying a Stacks application - the deployment workflow (build → deploy), pre/post deploy hooks, server vs serverless mode selection, first-time deployment setup, deployment troubleshooting, or the buddy deploy command. For cloud infrastructure details (EC2, Lambda, CloudFormation, Route53, IAM), see stacks-cloud.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript, AWS
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-desktop
|
|
3
|
-
description: Use when building or publishing desktop applications with Stacks
|
|
3
|
+
description: Use when building or publishing desktop applications with Stacks - Craft native windows, system tray, desktop packaging, or Mac App Store delivery.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-development
|
|
3
|
-
description: Use when setting up or configuring the Stacks development environment
|
|
3
|
+
description: Use when setting up or configuring the Stacks development environment - dev server, hot reload, development utilities, or IDE configuration. Covers the @stacksjs/development package, the dev server, CLI commands, reverse proxy, SSL, and dev workflow.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-dns
|
|
3
|
-
description: Use when managing DNS in a Stacks application
|
|
3
|
+
description: Use when managing DNS in a Stacks application - Route53 hosted zones, domain records, nameserver management, or DNS configuration. Covers @stacksjs/dns (AWS Route53 driver), @stacksjs/dnsx, and config/dns.ts.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: stacks-docs
|
|
3
|
-
description: Use when building or configuring documentation for a Stacks project
|
|
3
|
+
description: Use when building or configuring documentation for a Stacks project - BunPress setup, doc generation, navigation, sidebar structure, or documentation meta/SEO. Covers @stacksjs/docs and config/docs.ts.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Bun >= 1.3.0, TypeScript
|
|
6
6
|
allowed-tools: Read Edit Write Bash Grep Glob
|