Warenkorb: signiertes Gast-Cookie, Zusammenführung beim Login, Serverpreise und erweiterbare Summenberechnung
Installieren
genpm add @core/cartWas du bekommst
- Quellcode in src/lib/cart/, 10 Dateien. (24,9 kB)
- KI-Regeln in src/lib/cart/AGENTS.md, dazu Regeldateien für die IDE.
- Umgebungsvariablen in .env.example ergänzt: CART_COOKIE_SECRET.
- Löst @core/auth, @core/catalog, @core/contracts, @core/db, @core/money für dich auf.
README
Dieses Paket hat keine README.
Genau das liest deine KI, wenn sie in src/lib/cart arbeitet. Sonst wird ihrem Kontext nichts hinzugefügt.
@core/cart — rules for AI agents
Purpose
Carts for guests (id in an HMAC-signed HttpOnly cookie) and users (merged on login), lines per variant with stock
and quantity checks, and computeTotals: lines are always priced from @core/catalog in the database, unavailable
items are dropped with an issues entry, then registered steps run (discounts 100, shipping 200, taxes 300).
No payments or orders. Tables: carts, cart_lines.
Map
index.ts— public API:getCart,getOrCreateCart,addLine,setLine,setDiscountCodes,setCartMeta,computeTotals,registerTotalsStep,applyOrderDiscount,registerPendingCodeUses,pendingCodeUses,pruneCarts.cart.ts— operations.totals.ts— pipeline and types.cookie.ts— signed cookie.adapters/hono.ts—cartRoutes({ currentUserId }).adapters/next.ts—cartRoute,cartLinesRoute,cartLineRoute,cartCodesRoute.
Integration
- Env:
CART_COOKIE_SECRET(≥ 32 chars). Migrations as insrc/lib/db/AGENTS.md. - Mount the JSON API at
/api/cartwithcurrentUserIdfrom @core/auth (orasync () => nullfor guest-only shops). Calls:GET /api/cart,POST /api/cart/lines {variantId, quantity},PUT /api/cart/lines/:variantId {quantity}(0 removes),PUT /api/cart/codes {codes}. - Server pages:
const cart = await getCart(cookieHeader, userId); const totals = cart && await computeTotals(cart). - Modules that change totals register a step once at startup:
registerTotalsStep({ name, order, run: (t) => … }); order discounts must go throughapplyOrderDiscountso they are allocated to lines. Modules that open payments before the order exists (@core/checkout) registerregisterPendingCodeUsesso usage-limited codes (@core/discounts) count in-flight payments as reserved. - Schedule
pruneCarts()daily (@core/jobs; with kit-store, itsstore.maintenancejob does it). - Verify: add a product, change its price in the admin, the cart shows the new price.
Conventions
- Totals shown and charged come from
computeTotalsonly; never from values sent by the browser. - Show every
issuesentry to the customer before checkout. - Money values are
{ amount, currency }in minor units (@core/money).
Don't
- Don't reserve stock in the cart (that happens when the order is created).
- Don't trust prices, discounts or totals in request bodies, and don't read the cart cookie without verifying it.
# @core/cart — rules for AI agents
## Purpose
Carts for guests (id in an HMAC-signed HttpOnly cookie) and users (merged on login), lines per variant with stock
and quantity checks, and `computeTotals`: lines are always priced from @core/catalog in the database, unavailable
items are dropped with an `issues` entry, then registered steps run (discounts 100, shipping 200, taxes 300).
No payments or orders. Tables: `carts`, `cart_lines`.
## Map
- `index.ts` — public API: `getCart`, `getOrCreateCart`, `addLine`, `setLine`, `setDiscountCodes`, `setCartMeta`, `computeTotals`, `registerTotalsStep`, `applyOrderDiscount`, `registerPendingCodeUses`, `pendingCodeUses`, `pruneCarts`.
- `cart.ts` — operations. `totals.ts` — pipeline and types. `cookie.ts` — signed cookie.
- `adapters/hono.ts` — `cartRoutes({ currentUserId })`. `adapters/next.ts` — `cartRoute`, `cartLinesRoute`, `cartLineRoute`, `cartCodesRoute`.
## Integration
1. Env: `CART_COOKIE_SECRET` (≥ 32 chars). Migrations as in `src/lib/db/AGENTS.md`.
2. Mount the JSON API at `/api/cart` with `currentUserId` from @core/auth (or `async () => null` for guest-only shops).
Calls: `GET /api/cart`, `POST /api/cart/lines {variantId, quantity}`, `PUT /api/cart/lines/:variantId {quantity}` (0 removes), `PUT /api/cart/codes {codes}`.
3. Server pages: `const cart = await getCart(cookieHeader, userId); const totals = cart && await computeTotals(cart)`.
4. Modules that change totals register a step once at startup: `registerTotalsStep({ name, order, run: (t) => … })`;
order discounts must go through `applyOrderDiscount` so they are allocated to lines.
Modules that open payments before the order exists (@core/checkout) register `registerPendingCodeUses` so
usage-limited codes (@core/discounts) count in-flight payments as reserved.
5. Schedule `pruneCarts()` daily (@core/jobs; with kit-store, its `store.maintenance` job does it).
6. Verify: add a product, change its price in the admin, the cart shows the new price.
## Conventions
- Totals shown and charged come from `computeTotals` only; never from values sent by the browser.
- Show every `issues` entry to the customer before checkout.
- Money values are `{ amount, currency }` in minor units (@core/money).
## Don't
- Don't reserve stock in the cart (that happens when the order is created).
- Don't trust prices, discounts or totals in request bodies, and don't read the cart cookie without verifying it.
Der genaue Baum, der nach .genpmignore eingebunden wird. Gepinnt an
// Tablas de @core/cart. Las recoge drizzle-kit vía src/lib/db/drizzle.config.ts.
import { index, integer, jsonb, pgTable, primaryKey, text, timestamp } from 'drizzle-orm/pg-core';
import { users } from '../auth/index.ts';
import { productVariants } from '../catalog/index.ts';
import { primaryId, timestamps } from '../db/index.ts';
export const carts = pgTable(
'carts',
{
id: primaryId('crt'),
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
currency: text('currency').notNull(),
/** Códigos introducidos por el cliente (los valida @core/discounts al calcular). */
discountCodes: jsonb('discount_codes').$type<string[]>().notNull().default([]),
/** Datos que eligen otros pasos (tarifa de envío, país…). */
meta: jsonb('meta').$type<Record<string, string>>().notNull().default({}),
expiresAt: timestamp('expires_at', { withTimezone: true, mode: 'date' }).notNull(),
...timestamps,
},
(t) => [index('carts_user_idx').on(t.userId), index('carts_expires_idx').on(t.expiresAt)],
);
export const cartLines = pgTable(
'cart_lines',
{
cartId: text('cart_id')
.notNull()
.references(() => carts.id, { onDelete: 'cascade' }),
variantId: text('variant_id')
.notNull()
.references(() => productVariants.id, { onDelete: 'cascade' }),
quantity: integer('quantity').notNull(),
...timestamps,
},
(t) => [primaryKey({ columns: [t.cartId, t.variantId] })],
);
export type Cart = typeof carts.$inferSelect;
Dieses Paket deklariert keine MCP-Server.
| Version | Commit | Veröffentlicht | Prüfung |
|---|---|---|---|
| 1.1.0 | 787ea88 | vor 4 Stunden | Prüfung bestanden |
- npm
- zod ^4.0.0
- vorgeschlagen
- GenPM schlägt den npm-Befehl vor und führt ihn nur aus, wenn du zustimmst.
- Prüfung
- Prüfung bestanden · 0 Befunde
- Commit
- v1.1.0 → 787ea88d1eb25e11257f051f971c0bcce52803f6 · nach dem Abruf verifiziert
- Skripte
- Keine. GenPM führt niemals Paketcode aus.
- Lizenz
- MIT
- Qualität
- 100/100
- Anerkannte Lizenzerfüllt
- AGENTS.md erklärt den Zweckerfüllt
- AGENTS.md enthält Integrationsschritteerfüllt
- AGENTS.md nennt Konventionen oder Verboteerfüllt
- Enthält Testserfüllt
- Sicherheitsscan bestandenerfüllt
- In den letzten 6 Monaten veröffentlichterfüllt
- Verifizierter Herausgebererfüllt
- Zusammenfassung und Schlagwörtererfüllt
- Meldung
- Stimmt etwas nicht?