@endora-commerce/mod-carts 0.100.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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/admin/components/CartApprovalPolicyPanel.d.ts +51 -0
- package/dist/admin/components/CartApprovalPolicyPanel.d.ts.map +1 -0
- package/dist/admin/components/CartApprovalPolicyPanel.js +59 -0
- package/dist/admin/components/CartApprovalPolicyPanel.js.map +1 -0
- package/dist/admin/index.d.ts +38 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +73 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/CartDetail.d.ts +3 -0
- package/dist/admin/pages/CartDetail.d.ts.map +1 -0
- package/dist/admin/pages/CartDetail.js +74 -0
- package/dist/admin/pages/CartDetail.js.map +1 -0
- package/dist/admin/pages/CartsList.d.ts +3 -0
- package/dist/admin/pages/CartsList.d.ts.map +1 -0
- package/dist/admin/pages/CartsList.js +90 -0
- package/dist/admin/pages/CartsList.js.map +1 -0
- package/dist/admin/zones/CartApprovalPolicy.d.ts +29 -0
- package/dist/admin/zones/CartApprovalPolicy.d.ts.map +1 -0
- package/dist/admin/zones/CartApprovalPolicy.js +11 -0
- package/dist/admin/zones/CartApprovalPolicy.js.map +1 -0
- package/dist/backend/cli/abandonment-sweep.d.ts +64 -0
- package/dist/backend/cli/abandonment-sweep.d.ts.map +1 -0
- package/dist/backend/cli/abandonment-sweep.js +17 -0
- package/dist/backend/cli/abandonment-sweep.js.map +1 -0
- package/dist/backend/entities/cart-audit-entry.entity.d.ts +29 -0
- package/dist/backend/entities/cart-audit-entry.entity.d.ts.map +1 -0
- package/dist/backend/entities/cart-audit-entry.entity.js +109 -0
- package/dist/backend/entities/cart-audit-entry.entity.js.map +1 -0
- package/dist/backend/entities/cart-item.entity.d.ts +42 -0
- package/dist/backend/entities/cart-item.entity.d.ts.map +1 -0
- package/dist/backend/entities/cart-item.entity.js +119 -0
- package/dist/backend/entities/cart-item.entity.js.map +1 -0
- package/dist/backend/entities/cart.entity.d.ts +96 -0
- package/dist/backend/entities/cart.entity.d.ts.map +1 -0
- package/dist/backend/entities/cart.entity.js +207 -0
- package/dist/backend/entities/cart.entity.js.map +1 -0
- package/dist/backend/index.d.ts +122 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +184 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +31 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +95 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.d.ts +101 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +620 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/routes.organization.d.ts +29 -0
- package/dist/backend/routes.organization.d.ts.map +1 -0
- package/dist/backend/routes.organization.js +150 -0
- package/dist/backend/routes.organization.js.map +1 -0
- package/dist/backend/services/cart-abandonment-settings.d.ts +34 -0
- package/dist/backend/services/cart-abandonment-settings.d.ts.map +1 -0
- package/dist/backend/services/cart-abandonment-settings.js +55 -0
- package/dist/backend/services/cart-abandonment-settings.js.map +1 -0
- package/dist/backend/services/cart-abandonment-worker.d.ts +110 -0
- package/dist/backend/services/cart-abandonment-worker.d.ts.map +1 -0
- package/dist/backend/services/cart-abandonment-worker.js +185 -0
- package/dist/backend/services/cart-abandonment-worker.js.map +1 -0
- package/dist/backend/services/cart-admin-service.d.ts +58 -0
- package/dist/backend/services/cart-admin-service.d.ts.map +1 -0
- package/dist/backend/services/cart-admin-service.js +397 -0
- package/dist/backend/services/cart-admin-service.js.map +1 -0
- package/dist/backend/services/cart-approval-service.d.ts +147 -0
- package/dist/backend/services/cart-approval-service.d.ts.map +1 -0
- package/dist/backend/services/cart-approval-service.js +242 -0
- package/dist/backend/services/cart-approval-service.js.map +1 -0
- package/dist/backend/services/cart-audit-service.d.ts +75 -0
- package/dist/backend/services/cart-audit-service.d.ts.map +1 -0
- package/dist/backend/services/cart-audit-service.js +70 -0
- package/dist/backend/services/cart-audit-service.js.map +1 -0
- package/dist/backend/services/cart-conversion-service.d.ts +104 -0
- package/dist/backend/services/cart-conversion-service.d.ts.map +1 -0
- package/dist/backend/services/cart-conversion-service.js +175 -0
- package/dist/backend/services/cart-conversion-service.js.map +1 -0
- package/dist/backend/services/cart-coupon-service.d.ts +129 -0
- package/dist/backend/services/cart-coupon-service.d.ts.map +1 -0
- package/dist/backend/services/cart-coupon-service.js +244 -0
- package/dist/backend/services/cart-coupon-service.js.map +1 -0
- package/dist/backend/services/cart-organization-visibility-service.d.ts +43 -0
- package/dist/backend/services/cart-organization-visibility-service.d.ts.map +1 -0
- package/dist/backend/services/cart-organization-visibility-service.js +98 -0
- package/dist/backend/services/cart-organization-visibility-service.js.map +1 -0
- package/dist/backend/services/cart-placement-apply-port.d.ts +38 -0
- package/dist/backend/services/cart-placement-apply-port.d.ts.map +1 -0
- package/dist/backend/services/cart-placement-apply-port.js +71 -0
- package/dist/backend/services/cart-placement-apply-port.js.map +1 -0
- package/dist/backend/services/cart-pricing-recompute.d.ts +62 -0
- package/dist/backend/services/cart-pricing-recompute.d.ts.map +1 -0
- package/dist/backend/services/cart-pricing-recompute.js +131 -0
- package/dist/backend/services/cart-pricing-recompute.js.map +1 -0
- package/dist/backend/services/cart-query-service.d.ts +29 -0
- package/dist/backend/services/cart-query-service.d.ts.map +1 -0
- package/dist/backend/services/cart-query-service.js +40 -0
- package/dist/backend/services/cart-query-service.js.map +1 -0
- package/dist/backend/services/cart-read-port.d.ts +40 -0
- package/dist/backend/services/cart-read-port.d.ts.map +1 -0
- package/dist/backend/services/cart-read-port.js +144 -0
- package/dist/backend/services/cart-read-port.js.map +1 -0
- package/dist/backend/services/cart-recompute-cache.d.ts +49 -0
- package/dist/backend/services/cart-recompute-cache.d.ts.map +1 -0
- package/dist/backend/services/cart-recompute-cache.js +56 -0
- package/dist/backend/services/cart-recompute-cache.js.map +1 -0
- package/dist/backend/services/cart-service.d.ts +165 -0
- package/dist/backend/services/cart-service.d.ts.map +1 -0
- package/dist/backend/services/cart-service.js +556 -0
- package/dist/backend/services/cart-service.js.map +1 -0
- package/dist/backend/services/cart-state-machine.d.ts +51 -0
- package/dist/backend/services/cart-state-machine.d.ts.map +1 -0
- package/dist/backend/services/cart-state-machine.js +74 -0
- package/dist/backend/services/cart-state-machine.js.map +1 -0
- package/dist/backend/services/cart-upsell-service.d.ts +57 -0
- package/dist/backend/services/cart-upsell-service.d.ts.map +1 -0
- package/dist/backend/services/cart-upsell-service.js +100 -0
- package/dist/backend/services/cart-upsell-service.js.map +1 -0
- package/dist/backend/services/customer-group-resolver.d.ts +27 -0
- package/dist/backend/services/customer-group-resolver.d.ts.map +1 -0
- package/dist/backend/services/customer-group-resolver.js +32 -0
- package/dist/backend/services/customer-group-resolver.js.map +1 -0
- package/dist/manifest.d.ts +221 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +224 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260611T140352_carts_consolidation.d.ts +43 -0
- package/dist/migrations/20260611T140352_carts_consolidation.d.ts.map +1 -0
- package/dist/migrations/20260611T140352_carts_consolidation.js +237 -0
- package/dist/migrations/20260611T140352_carts_consolidation.js.map +1 -0
- package/dist/migrations/20260611T140413_carts_cart_item_packaging.d.ts +12 -0
- package/dist/migrations/20260611T140413_carts_cart_item_packaging.d.ts.map +1 -0
- package/dist/migrations/20260611T140413_carts_cart_item_packaging.js +20 -0
- package/dist/migrations/20260611T140413_carts_cart_item_packaging.js.map +1 -0
- package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.d.ts +50 -0
- package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.d.ts.map +1 -0
- package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.js +62 -0
- package/dist/migrations/20260818T081253_carts_cart_completed_order_fk.js.map +1 -0
- package/dist/migrations/20260830T112911_carts_organization_attribution_check.d.ts +67 -0
- package/dist/migrations/20260830T112911_carts_organization_attribution_check.d.ts.map +1 -0
- package/dist/migrations/20260830T112911_carts_organization_attribution_check.js +121 -0
- package/dist/migrations/20260830T112911_carts_organization_attribution_check.js.map +1 -0
- package/dist/migrations/index.d.ts +30 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +35 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +106 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/carts.md +232 -0
- package/i18n/en.json +96 -0
- package/i18n/pl.json +96 -0
- package/package.json +107 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
import { addCartItemRequestSchema, updateCartItemRequestSchema, saveCartItemToListSchema, cartUpsellsQuerySchema, applyCartCouponSchema, convertCartToQrSchema, ERROR_CODES, OrganizationCannotTransactError, } from '@endora-commerce/contracts';
|
|
2
|
+
import { derivePrimaryCta } from './services/cart-state-machine.js';
|
|
3
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
4
|
+
import { productAudienceOf } from '@endora-commerce/platform/http';
|
|
5
|
+
import { rethrowIfModuleDisabled } from '@endora-commerce/platform/kernel';
|
|
6
|
+
const ANON_COOKIE = 'b2b_cart_anon';
|
|
7
|
+
/**
|
|
8
|
+
* Cart routes — open to anonymous users too. The resolver picks the right
|
|
9
|
+
* cart based on (authenticated session, anonymous cookie, neither).
|
|
10
|
+
*
|
|
11
|
+
* Anonymous callers receive a Set-Cookie for `b2b_cart_anon` on first add so
|
|
12
|
+
* later requests target the same cart.
|
|
13
|
+
*/
|
|
14
|
+
export async function registerCartRoutes(app, deps) {
|
|
15
|
+
const { cartService, resolveCartActor, emFactory } = deps;
|
|
16
|
+
const { assertOrganizationCanTransact } = deps;
|
|
17
|
+
/**
|
|
18
|
+
* Refuses to mutate a cart when the signed-in Customer's Organization is
|
|
19
|
+
* not active. Anonymous carts are always permitted (no Organization is
|
|
20
|
+
* attached to them).
|
|
21
|
+
*/
|
|
22
|
+
const guardOrganizationTransact = async (actor) => {
|
|
23
|
+
if (!assertOrganizationCanTransact)
|
|
24
|
+
return;
|
|
25
|
+
if (!actor.customer)
|
|
26
|
+
return;
|
|
27
|
+
// No-org Customer accounts (feature 026 US2) bypass the gate — they
|
|
28
|
+
// already use platform defaults and have no Organization status to fail
|
|
29
|
+
// against. Cart-add stays open.
|
|
30
|
+
if (!actor.customer.organizationId)
|
|
31
|
+
return;
|
|
32
|
+
try {
|
|
33
|
+
await assertOrganizationCanTransact(actor.customer.organizationId);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (err instanceof OrganizationCannotTransactError) {
|
|
37
|
+
throw new HttpError(423, ERROR_CODES.FORBIDDEN, 'Your Organization cannot transact in its current status.', { code: 'organization_cannot_transact', status: err.status });
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
app.get('/api/v1/cart', async (request) => {
|
|
43
|
+
const actor = resolveCartActor(request);
|
|
44
|
+
const em = emFactory();
|
|
45
|
+
let cart = null;
|
|
46
|
+
if (actor.customer) {
|
|
47
|
+
cart = await cartService.getOrCreateForCustomer(actor.customer);
|
|
48
|
+
}
|
|
49
|
+
else if (actor.anonymousToken) {
|
|
50
|
+
cart = await cartService.getOrCreateForAnon(em, actor.anonymousToken);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return request.query?.view === 'mini'
|
|
54
|
+
? { data: emptyMiniCart() }
|
|
55
|
+
: { data: emptyCart() };
|
|
56
|
+
}
|
|
57
|
+
const items = await cartService.getItems(cart.id);
|
|
58
|
+
// Feature 027 FR-007 — mini-cart payload. Skips re-pricing-on-read,
|
|
59
|
+
// skips coupon re-evaluation, and emits the lightweight serializer
|
|
60
|
+
// (no per-line `unavailable*`, no `droppedLines`, no
|
|
61
|
+
// `couponDroppedThisRead`). The mini endpoint is on the storefront's
|
|
62
|
+
// hot path (every header render) so the heavy work is intentionally
|
|
63
|
+
// deferred to the full view.
|
|
64
|
+
if (request.query?.view === 'mini') {
|
|
65
|
+
return { data: serializeCartMini(cart, items) };
|
|
66
|
+
}
|
|
67
|
+
// Resolve per-line display name + slug so the storefront does not
|
|
68
|
+
// have to fan out one fetch per line. Bulk lookup keyed by
|
|
69
|
+
// productId; locale comes from Accept-Language (first tag wins),
|
|
70
|
+
// English/Polish are the documented fallback chain.
|
|
71
|
+
const preferredLanguage = parsePreferredLanguage(request.headers['accept-language']);
|
|
72
|
+
// Never block a cart read on a product-meta lookup hiccup (schema drift,
|
|
73
|
+
// a malformed product row, a since-deleted product): fall back to empty
|
|
74
|
+
// meta so lines still render with null names rather than 500ing the whole
|
|
75
|
+
// cart. Mirrors the recompute/coupon/promotion guards below.
|
|
76
|
+
let productMeta;
|
|
77
|
+
try {
|
|
78
|
+
productMeta = await loadProductMeta(deps.catalogProducts, items.map((it) => it.productId), preferredLanguage);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
request.log.warn({ err, cartId: cart.id }, 'cart product-meta lookup failed; rendering without names');
|
|
82
|
+
productMeta = new Map();
|
|
83
|
+
}
|
|
84
|
+
// Feature 027 §R5 — re-pricing on read (FR-008).
|
|
85
|
+
let recomputedPrices = null;
|
|
86
|
+
if (deps.cartPricingRecompute && items.length > 0) {
|
|
87
|
+
try {
|
|
88
|
+
const lines = items.map((it) => ({
|
|
89
|
+
cartItemId: it.id,
|
|
90
|
+
productId: it.productId,
|
|
91
|
+
variantId: it.variantId ?? null,
|
|
92
|
+
quantity: it.quantity,
|
|
93
|
+
}));
|
|
94
|
+
const resolved = await deps.cartPricingRecompute.recompute({
|
|
95
|
+
cartId: cart.id,
|
|
96
|
+
organizationId: cart.organizationId ?? null,
|
|
97
|
+
salesChannelId: cart.salesChannelId ?? null,
|
|
98
|
+
}, lines);
|
|
99
|
+
recomputedPrices = new Map(resolved
|
|
100
|
+
.filter((r) => r.amount !== null)
|
|
101
|
+
.map((r) => [r.cartItemId, { amount: r.amount, currency: r.currency }]));
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
// The tolerance is narrow and stays: a resolver hiccup on one read must
|
|
105
|
+
// not take the whole cart down, and the snapshotted unit price is a
|
|
106
|
+
// figure the platform actually quoted.
|
|
107
|
+
//
|
|
108
|
+
// What it may not absorb is the presence answer (issue #124). `carts`
|
|
109
|
+
// declares `price_lists` a hard dependency, so an absent one fails
|
|
110
|
+
// closed — and this `catch` was quietly deciding the opposite: it
|
|
111
|
+
// rendered every line at its snapshot with `price_lists` switched off
|
|
112
|
+
// and said nothing, so the buyer read prices from a module the platform
|
|
113
|
+
// was refusing to serve and found out at checkout.
|
|
114
|
+
rethrowIfModuleDisabled(err);
|
|
115
|
+
request.log.warn({ err, cartId: cart.id }, 'cart repricing failed; rendering snapshots');
|
|
116
|
+
recomputedPrices = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Feature 027 US2 / FR-014 — re-evaluate the applied coupon on every
|
|
120
|
+
// read. If a previously-valid code is no longer eligible (e.g. the
|
|
121
|
+
// cart fell below min spend after a line removal), drop it silently
|
|
122
|
+
// on the cart record and surface `couponDroppedThisRead` so the
|
|
123
|
+
// storefront banner can inform the buyer.
|
|
124
|
+
let couponDroppedThisRead = null;
|
|
125
|
+
if (deps.cartCouponService && cart.appliedPromotionCode && items.length > 0) {
|
|
126
|
+
try {
|
|
127
|
+
const result = await deps.cartCouponService.reevaluateOnRead(cart, items);
|
|
128
|
+
if (result.dropped) {
|
|
129
|
+
couponDroppedThisRead = result.dropped;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
// Never block a cart read on a coupon-engine hiccup — but an absent
|
|
134
|
+
// `promotions` is not a hiccup (issue #124). Swallowing it charged the
|
|
135
|
+
// buyer the undiscounted total while the cart went on showing the code.
|
|
136
|
+
rethrowIfModuleDisabled(err);
|
|
137
|
+
couponDroppedThisRead = null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Feature 045 — compute the real discount + per-promotion breakdown on
|
|
141
|
+
// read so the cart shows applied promotions (FR-031/FR-033). Never block
|
|
142
|
+
// a cart read on a promotion-engine hiccup.
|
|
143
|
+
let application = null;
|
|
144
|
+
if (deps.cartCouponService && items.length > 0) {
|
|
145
|
+
try {
|
|
146
|
+
application = await deps.cartCouponService.computeApplication(cart, items);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
// Same rule as the coupon re-evaluation above: a hiccup degrades to "no
|
|
150
|
+
// breakdown", an absent `promotions` does not degrade at all.
|
|
151
|
+
rethrowIfModuleDisabled(err);
|
|
152
|
+
application = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
data: serializeCart(cart, items, recomputedPrices, couponDroppedThisRead, productMeta, application),
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
app.post('/api/v1/cart/items', { schema: { body: addCartItemRequestSchema } }, async (request, reply) => {
|
|
160
|
+
let actor = resolveCartActor(request);
|
|
161
|
+
// If neither side identified, mint an anonymous token and echo it back.
|
|
162
|
+
if (!actor.customer && !actor.anonymousToken) {
|
|
163
|
+
const token = `anon-${crypto.randomUUID()}`;
|
|
164
|
+
reply.setCookie(ANON_COOKIE, token, { path: '/', sameSite: 'lax', httpOnly: true });
|
|
165
|
+
actor = { anonymousToken: token };
|
|
166
|
+
}
|
|
167
|
+
await guardOrganizationTransact(actor);
|
|
168
|
+
const body = addCartItemRequestSchema.parse(request.body);
|
|
169
|
+
const result = await cartService.addItem(actor, {
|
|
170
|
+
productId: body.productId,
|
|
171
|
+
...(body.variantId ? { variantId: body.variantId } : {}),
|
|
172
|
+
quantity: body.quantity,
|
|
173
|
+
...(body.packagingUnitId ? { packagingUnitId: body.packagingUnitId } : {}),
|
|
174
|
+
});
|
|
175
|
+
return { data: serializeCart(result.cart, result.items) };
|
|
176
|
+
});
|
|
177
|
+
app.patch('/api/v1/cart/items/:itemId', { schema: { body: updateCartItemRequestSchema } }, async (request) => {
|
|
178
|
+
const actor = resolveCartActor(request);
|
|
179
|
+
const body = updateCartItemRequestSchema.parse(request.body);
|
|
180
|
+
const result = await cartService.updateItem(actor, request.params.itemId, body.quantity);
|
|
181
|
+
return { data: serializeCart(result.cart, result.items) };
|
|
182
|
+
});
|
|
183
|
+
app.delete('/api/v1/cart/items/:itemId', async (request) => {
|
|
184
|
+
const actor = resolveCartActor(request);
|
|
185
|
+
const result = await cartService.removeItem(actor, request.params.itemId);
|
|
186
|
+
return { data: serializeCart(result.cart, result.items) };
|
|
187
|
+
});
|
|
188
|
+
// ── Feature 027 — new endpoints (touch / save-to-shopping-list / upsells)
|
|
189
|
+
/**
|
|
190
|
+
* Explicit "buyer opened the cart page" ping. Bumps last_activity_at;
|
|
191
|
+
* reactivates an abandoned cart back to active. No-op on missing cart
|
|
192
|
+
* (no error — the storefront calls this on every cart-page mount).
|
|
193
|
+
*/
|
|
194
|
+
app.post('/api/v1/cart/touch', async (request, reply) => {
|
|
195
|
+
const actor = resolveCartActor(request);
|
|
196
|
+
if (!actor.customer && !actor.anonymousToken) {
|
|
197
|
+
reply.code(204);
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
await cartService.touch(actor);
|
|
201
|
+
reply.code(204);
|
|
202
|
+
return null;
|
|
203
|
+
});
|
|
204
|
+
/**
|
|
205
|
+
* Push one cart line into the named Purchase List. Cross-module call
|
|
206
|
+
* goes through the optional `pushLineToShoppingList` port; if not
|
|
207
|
+
* wired, the route returns HTTP 503 so the storefront surfaces the
|
|
208
|
+
* "feature unavailable" message instead of silently dropping.
|
|
209
|
+
*/
|
|
210
|
+
app.post('/api/v1/cart/items/:itemId/save-to-shopping-list', { schema: { body: saveCartItemToListSchema } }, async (request, reply) => {
|
|
211
|
+
const actor = resolveCartActor(request);
|
|
212
|
+
if (!actor.customer) {
|
|
213
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Sign-in required to save to a shopping list.');
|
|
214
|
+
}
|
|
215
|
+
if (!deps.pushLineToShoppingList) {
|
|
216
|
+
throw new HttpError(503, ERROR_CODES.NOT_FOUND, 'shopping_lists_unavailable');
|
|
217
|
+
}
|
|
218
|
+
const body = saveCartItemToListSchema.parse(request.body);
|
|
219
|
+
const item = await emFactory().findOne((await import('./entities/cart-item.entity.js')).CartItem, { id: request.params.itemId });
|
|
220
|
+
if (!item) {
|
|
221
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Cart item not found.');
|
|
222
|
+
}
|
|
223
|
+
// Ownership: only the cart's owner may save its line.
|
|
224
|
+
const cart = await emFactory().findOne((await import('./entities/cart.entity.js')).Cart, { id: item.cartId });
|
|
225
|
+
if (!cart || cart.customerAccountId !== actor.customer.customerAccountId) {
|
|
226
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Cart item not found.');
|
|
227
|
+
}
|
|
228
|
+
await deps.pushLineToShoppingList({
|
|
229
|
+
customerAccountId: actor.customer.customerAccountId,
|
|
230
|
+
organizationId: actor.customer.organizationId,
|
|
231
|
+
shoppingListId: body.shoppingListId,
|
|
232
|
+
productId: item.productId,
|
|
233
|
+
variantId: item.variantId ?? null,
|
|
234
|
+
quantity: item.quantity,
|
|
235
|
+
});
|
|
236
|
+
reply.code(204);
|
|
237
|
+
return null;
|
|
238
|
+
});
|
|
239
|
+
/**
|
|
240
|
+
* Apply (or replace, or clear) the active coupon code on the cart.
|
|
241
|
+
* Returns the recomputed full cart on success; HTTP 422 with a
|
|
242
|
+
* reason-specific body on rejection.
|
|
243
|
+
*/
|
|
244
|
+
app.post('/api/v1/cart/coupon', { schema: { body: applyCartCouponSchema } }, async (request) => {
|
|
245
|
+
const actor = resolveCartActor(request);
|
|
246
|
+
const em = emFactory();
|
|
247
|
+
let cart = null;
|
|
248
|
+
if (actor.customer) {
|
|
249
|
+
cart = await cartService.getOrCreateForCustomer(actor.customer);
|
|
250
|
+
}
|
|
251
|
+
else if (actor.anonymousToken) {
|
|
252
|
+
cart = await cartService.getOrCreateForAnon(em, actor.anonymousToken);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Cart not found.');
|
|
256
|
+
}
|
|
257
|
+
const items = await cartService.getItems(cart.id);
|
|
258
|
+
if (items.length === 0) {
|
|
259
|
+
throw new HttpError(422, ERROR_CODES.CART_EMPTY, 'cart_empty');
|
|
260
|
+
}
|
|
261
|
+
if (!deps.cartCouponService) {
|
|
262
|
+
throw new HttpError(503, ERROR_CODES.NOT_FOUND, 'promotions_unavailable');
|
|
263
|
+
}
|
|
264
|
+
const body = applyCartCouponSchema.parse(request.body);
|
|
265
|
+
const result = await deps.cartCouponService.apply(cart, body.code);
|
|
266
|
+
if (result.outcome === 'dropped') {
|
|
267
|
+
// Issue #231 — the discriminator goes in **both** fields, and the
|
|
268
|
+
// duplication is the point.
|
|
269
|
+
//
|
|
270
|
+
// `reason` is what the published contract carries
|
|
271
|
+
// (`couponFailureBodySchema` in `packages/contracts/src/carts.ts`) and
|
|
272
|
+
// what the storefront branches on. `code` is what the envelope's
|
|
273
|
+
// `refusalToken` reads (`src/http/error-envelope.ts`) to build
|
|
274
|
+
// `errors.CART_COUPON_REJECTED.<token>` — the keys this module's bundle
|
|
275
|
+
// files the seven written reason sentences under. Spelling the
|
|
276
|
+
// discriminator only `reason` is why every one of them was dead: a
|
|
277
|
+
// buyer under the minimum spend was shown the generic refusal while a
|
|
278
|
+
// translated sentence telling them what to do about it sat unread.
|
|
279
|
+
//
|
|
280
|
+
// Additive on purpose. Renaming `reason` to `code` would be one field
|
|
281
|
+
// instead of two and would break a shape two schemas already publish.
|
|
282
|
+
const detailsBody = {
|
|
283
|
+
code: result.reason,
|
|
284
|
+
reason: result.reason,
|
|
285
|
+
};
|
|
286
|
+
if ('shortfall' in result && result.shortfall) {
|
|
287
|
+
detailsBody['shortfall'] = result.shortfall;
|
|
288
|
+
}
|
|
289
|
+
throw new HttpError(422, ERROR_CODES.CART_COUPON_REJECTED, 'CART_COUPON_REJECTED', detailsBody);
|
|
290
|
+
}
|
|
291
|
+
const freshItems = await cartService.getItems(cart.id);
|
|
292
|
+
return { data: serializeCart(cart, freshItems) };
|
|
293
|
+
});
|
|
294
|
+
/**
|
|
295
|
+
* Convenience alias for `POST /api/v1/cart/coupon` with `code=null`.
|
|
296
|
+
*/
|
|
297
|
+
app.delete('/api/v1/cart/coupon', async (request) => {
|
|
298
|
+
const actor = resolveCartActor(request);
|
|
299
|
+
const em = emFactory();
|
|
300
|
+
let cart = null;
|
|
301
|
+
if (actor.customer) {
|
|
302
|
+
cart = await cartService.getOrCreateForCustomer(actor.customer);
|
|
303
|
+
}
|
|
304
|
+
else if (actor.anonymousToken) {
|
|
305
|
+
cart = await cartService.getOrCreateForAnon(em, actor.anonymousToken);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Cart not found.');
|
|
309
|
+
}
|
|
310
|
+
if (deps.cartCouponService) {
|
|
311
|
+
await deps.cartCouponService.clear(cart);
|
|
312
|
+
}
|
|
313
|
+
const items = await cartService.getItems(cart.id);
|
|
314
|
+
return { data: serializeCart(cart, items) };
|
|
315
|
+
});
|
|
316
|
+
// ── Feature 027 US3 — three conversions
|
|
317
|
+
/**
|
|
318
|
+
* Cart → Quote Request. Refuses on an empty cart or unauthenticated
|
|
319
|
+
* caller. Source cart flips to `completed` with
|
|
320
|
+
* `converted_to_quote_request_id` set.
|
|
321
|
+
*/
|
|
322
|
+
app.post('/api/v1/cart/convert-to-quote-request', { schema: { body: convertCartToQrSchema } }, async (request) => {
|
|
323
|
+
const actor = resolveCartActor(request);
|
|
324
|
+
if (!actor.customer) {
|
|
325
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Sign-in required.');
|
|
326
|
+
}
|
|
327
|
+
if (!deps.cartConversionService) {
|
|
328
|
+
throw new HttpError(503, ERROR_CODES.NOT_FOUND, 'quote_requests_unavailable');
|
|
329
|
+
}
|
|
330
|
+
// D-178 — an invariant, not a business state; see
|
|
331
|
+
// `routes.organization.ts`'s twin.
|
|
332
|
+
if (!actor.customer.organizationId) {
|
|
333
|
+
throw new HttpError(500, ERROR_CODES.INTERNAL, 'Invariant violated: a customer account has no Organization (Principle XI).');
|
|
334
|
+
}
|
|
335
|
+
const cart = await cartService.getOrCreateForCustomer(actor.customer);
|
|
336
|
+
const body = convertCartToQrSchema.parse(request.body ?? {});
|
|
337
|
+
const result = await deps.cartConversionService.convertToQuoteRequest(cart, {
|
|
338
|
+
customerAccountId: actor.customer.customerAccountId,
|
|
339
|
+
organizationId: actor.customer.organizationId,
|
|
340
|
+
isOrgAdmin: false,
|
|
341
|
+
}, body.note);
|
|
342
|
+
return {
|
|
343
|
+
data: {
|
|
344
|
+
quoteRequestId: result.quoteRequestId,
|
|
345
|
+
cartId: result.cartId,
|
|
346
|
+
quoteRequestSlug: result.quoteRequestId,
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
});
|
|
350
|
+
/**
|
|
351
|
+
* Quote Request → Cart. Re-prices from the customer's current list;
|
|
352
|
+
* unavailable / no-price lines are dropped and returned to the
|
|
353
|
+
* caller.
|
|
354
|
+
*/
|
|
355
|
+
app.post('/api/v1/cart/from-quote-request/:quoteRequestId', async (request) => {
|
|
356
|
+
const actor = resolveCartActor(request);
|
|
357
|
+
if (!actor.customer) {
|
|
358
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Sign-in required.');
|
|
359
|
+
}
|
|
360
|
+
if (!deps.cartConversionService) {
|
|
361
|
+
throw new HttpError(503, ERROR_CODES.NOT_FOUND, 'quote_requests_unavailable');
|
|
362
|
+
}
|
|
363
|
+
const result = await deps.cartConversionService.createCartFromQuoteRequest(request.params.quoteRequestId, actor.customer);
|
|
364
|
+
return { data: result };
|
|
365
|
+
});
|
|
366
|
+
/**
|
|
367
|
+
* Shopping List → Cart. Delegates to ShoppingListService.convertToCart
|
|
368
|
+
* through the `appendShoppingListToCart` port. Composition wires that
|
|
369
|
+
* port at boot time.
|
|
370
|
+
*/
|
|
371
|
+
app.post('/api/v1/cart/from-shopping-list/:shoppingListId', async (request) => {
|
|
372
|
+
const actor = resolveCartActor(request);
|
|
373
|
+
if (!actor.customer) {
|
|
374
|
+
throw new HttpError(401, ERROR_CODES.UNAUTHORIZED, 'Sign-in required.');
|
|
375
|
+
}
|
|
376
|
+
if (!deps.appendShoppingListToCart) {
|
|
377
|
+
throw new HttpError(503, ERROR_CODES.NOT_FOUND, 'shopping_lists_unavailable');
|
|
378
|
+
}
|
|
379
|
+
const result = await deps.appendShoppingListToCart({
|
|
380
|
+
customerAccountId: actor.customer.customerAccountId,
|
|
381
|
+
organizationId: actor.customer.organizationId,
|
|
382
|
+
shoppingListId: request.params.shoppingListId,
|
|
383
|
+
});
|
|
384
|
+
return { data: result };
|
|
385
|
+
});
|
|
386
|
+
/**
|
|
387
|
+
* Up-sell strip for the full cart view. Aggregator over Catalog's
|
|
388
|
+
* `product_links` (kind = 'up_sell'); excludes products already in
|
|
389
|
+
* the cart; ranks by match-count desc, tie-break oldest-first.
|
|
390
|
+
*/
|
|
391
|
+
app.get('/api/v1/cart/upsells', async (request) => {
|
|
392
|
+
if (!deps.cartUpsellService) {
|
|
393
|
+
return { data: [] };
|
|
394
|
+
}
|
|
395
|
+
const actor = resolveCartActor(request);
|
|
396
|
+
const em = emFactory();
|
|
397
|
+
let cart = null;
|
|
398
|
+
if (actor.customer) {
|
|
399
|
+
cart = await cartService.getOrCreateForCustomer(actor.customer);
|
|
400
|
+
}
|
|
401
|
+
else if (actor.anonymousToken) {
|
|
402
|
+
cart = await cartService.getOrCreateForAnon(em, actor.anonymousToken);
|
|
403
|
+
}
|
|
404
|
+
if (!cart)
|
|
405
|
+
return { data: [] };
|
|
406
|
+
const parsed = cartUpsellsQuerySchema.parse(request.query ?? {});
|
|
407
|
+
const limit = parsed.limit ?? 12;
|
|
408
|
+
const candidates = await deps.cartUpsellService.forCart(cart.id, limit, productAudienceOf(request));
|
|
409
|
+
return { data: candidates };
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function emptyCart() {
|
|
413
|
+
return {
|
|
414
|
+
id: null,
|
|
415
|
+
customerAccountId: null,
|
|
416
|
+
organizationId: null,
|
|
417
|
+
salesChannelId: null,
|
|
418
|
+
anonymousCartToken: null,
|
|
419
|
+
status: 'active',
|
|
420
|
+
approvalStatus: 'not_required',
|
|
421
|
+
items: [],
|
|
422
|
+
itemCount: 0,
|
|
423
|
+
subtotal: { amount: 0, currency: 'PLN' },
|
|
424
|
+
discount: null,
|
|
425
|
+
appliedPromotions: [],
|
|
426
|
+
grandTotal: { amount: 0, currency: 'PLN' },
|
|
427
|
+
primaryCta: 'checkout',
|
|
428
|
+
droppedLines: [],
|
|
429
|
+
couponDroppedThisRead: null,
|
|
430
|
+
createdAt: null,
|
|
431
|
+
updatedAt: null,
|
|
432
|
+
lastActivityAt: null,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function emptyMiniCart() {
|
|
436
|
+
return {
|
|
437
|
+
id: null,
|
|
438
|
+
itemCount: 0,
|
|
439
|
+
items: [],
|
|
440
|
+
subtotal: { amount: 0, currency: 'PLN' },
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Mini-cart payload (Feature 027 FR-007). Strips the heavy parts of the
|
|
445
|
+
* full view: no re-pricing-on-read, no coupon re-evaluation, no per-line
|
|
446
|
+
* `unavailable*`, no `droppedLines`, no `couponDroppedThisRead`, no
|
|
447
|
+
* `primaryCta`. The header renders this on every page navigation so the
|
|
448
|
+
* envelope is intentionally minimal.
|
|
449
|
+
*/
|
|
450
|
+
function serializeCartMini(cart, items) {
|
|
451
|
+
let subtotal = 0;
|
|
452
|
+
const currency = items[0]?.currency ?? 'PLN';
|
|
453
|
+
const serializedItems = items.map((it) => {
|
|
454
|
+
const unitPriceAmount = Number(it.unitPrice);
|
|
455
|
+
subtotal += unitPriceAmount * it.quantity;
|
|
456
|
+
return {
|
|
457
|
+
id: it.id,
|
|
458
|
+
productId: it.productId,
|
|
459
|
+
variantId: it.variantId ?? null,
|
|
460
|
+
quantity: it.quantity,
|
|
461
|
+
unitPrice: { amount: unitPriceAmount, currency: it.currency },
|
|
462
|
+
lineTotal: { amount: unitPriceAmount * it.quantity, currency: it.currency },
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
return {
|
|
466
|
+
id: cart.id,
|
|
467
|
+
itemCount: items.length,
|
|
468
|
+
items: serializedItems,
|
|
469
|
+
subtotal: { amount: subtotal, currency },
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function serializeCart(cart, items, recomputedPrices = null, couponDroppedThisRead = null, productMeta = new Map(), application = null) {
|
|
473
|
+
let subtotal = 0;
|
|
474
|
+
let currency = items[0]?.currency ?? 'PLN';
|
|
475
|
+
const serializedItems = items.map((it) => {
|
|
476
|
+
const recomputed = recomputedPrices?.get(it.id);
|
|
477
|
+
const unitPriceAmount = recomputed ? recomputed.amount : Number(it.unitPrice);
|
|
478
|
+
const lineCurrency = recomputed ? recomputed.currency : it.currency;
|
|
479
|
+
if (currency === 'PLN' && lineCurrency !== 'PLN')
|
|
480
|
+
currency = lineCurrency;
|
|
481
|
+
subtotal += unitPriceAmount * it.quantity;
|
|
482
|
+
const meta = productMeta.get(it.productId) ?? null;
|
|
483
|
+
// Feature 043 — append the packaging-unit name to the display name when the
|
|
484
|
+
// line was added as a packaging unit (e.g. "<name> (Paleta)").
|
|
485
|
+
const packagingUnitName = it.packagingUnitName ?? null;
|
|
486
|
+
const baseName = meta?.name ?? null;
|
|
487
|
+
const displayName = baseName !== null && packagingUnitName
|
|
488
|
+
? `${baseName} (${packagingUnitName})`
|
|
489
|
+
: baseName;
|
|
490
|
+
return {
|
|
491
|
+
id: it.id,
|
|
492
|
+
productId: it.productId,
|
|
493
|
+
variantId: it.variantId ?? null,
|
|
494
|
+
quantity: it.quantity,
|
|
495
|
+
unitPrice: { amount: unitPriceAmount, currency: lineCurrency },
|
|
496
|
+
lineTotal: { amount: unitPriceAmount * it.quantity, currency: lineCurrency },
|
|
497
|
+
unavailable: false,
|
|
498
|
+
unavailableReason: null,
|
|
499
|
+
productName: baseName,
|
|
500
|
+
productSlug: meta?.slug ?? null,
|
|
501
|
+
productSku: meta?.sku ?? null,
|
|
502
|
+
packagingUnitName,
|
|
503
|
+
packagingUnitBaseQuantity: it.packagingUnitBaseQuantity ?? null,
|
|
504
|
+
displayName,
|
|
505
|
+
};
|
|
506
|
+
});
|
|
507
|
+
return {
|
|
508
|
+
id: cart.id,
|
|
509
|
+
customerAccountId: cart.customerAccountId ?? null,
|
|
510
|
+
organizationId: cart.organizationId ?? null,
|
|
511
|
+
salesChannelId: cart.salesChannelId ?? null,
|
|
512
|
+
anonymousCartToken: cart.anonymousCartToken ?? null,
|
|
513
|
+
status: cart.status,
|
|
514
|
+
approvalStatus: cart.approvalStatus,
|
|
515
|
+
items: serializedItems,
|
|
516
|
+
itemCount: items.length,
|
|
517
|
+
subtotal: { amount: subtotal, currency },
|
|
518
|
+
discount: application && application.discountTotal > 0
|
|
519
|
+
? { code: cart.appliedPromotionCode ?? null, amount: application.discountTotal, currency }
|
|
520
|
+
: cart.appliedPromotionCode
|
|
521
|
+
? { code: cart.appliedPromotionCode, amount: 0, currency }
|
|
522
|
+
: null,
|
|
523
|
+
appliedPromotions: application
|
|
524
|
+
? application.appliedPromotions.map((ap) => ({
|
|
525
|
+
promotionId: ap.promotionId,
|
|
526
|
+
couponId: ap.couponId ?? null,
|
|
527
|
+
amount: ap.amount,
|
|
528
|
+
currency,
|
|
529
|
+
}))
|
|
530
|
+
: [],
|
|
531
|
+
grandTotal: {
|
|
532
|
+
amount: Math.max(0, round2Cart(subtotal - (application?.discountTotal ?? 0))),
|
|
533
|
+
currency,
|
|
534
|
+
},
|
|
535
|
+
primaryCta: resolvePrimaryCta(cart),
|
|
536
|
+
droppedLines: [],
|
|
537
|
+
couponDroppedThisRead,
|
|
538
|
+
createdAt: cart.createdAt.toISOString(),
|
|
539
|
+
updatedAt: cart.updatedAt.toISOString(),
|
|
540
|
+
lastActivityAt: cart.lastActivityAt.toISOString(),
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function round2Cart(n) {
|
|
544
|
+
return Math.round(n * 100) / 100;
|
|
545
|
+
}
|
|
546
|
+
function resolvePrimaryCta(cart) {
|
|
547
|
+
// The `submit_for_approval` branch needs the Organization's policy
|
|
548
|
+
// flag, which the route handler does not have on hand here. The
|
|
549
|
+
// helper is callable from the org-admin route with the flag passed
|
|
550
|
+
// through explicitly; for the storefront read we default to
|
|
551
|
+
// `requiresApproval: false`, which makes `not_required` + cart-active
|
|
552
|
+
// collapse to `checkout`.
|
|
553
|
+
return derivePrimaryCta({
|
|
554
|
+
status: cart.status,
|
|
555
|
+
approvalStatus: cart.approvalStatus,
|
|
556
|
+
requiresApproval: false,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Resolves the storefront's preferred language from `Accept-Language`.
|
|
561
|
+
* Falls back to undefined when the header is missing — the caller
|
|
562
|
+
* then walks the product record's `name` locale chain in serializeCart's helper.
|
|
563
|
+
*/
|
|
564
|
+
function parsePreferredLanguage(header) {
|
|
565
|
+
const raw = Array.isArray(header) ? header[0] : header;
|
|
566
|
+
if (!raw)
|
|
567
|
+
return undefined;
|
|
568
|
+
const first = raw.split(',')[0]?.trim();
|
|
569
|
+
return first || undefined;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Bulk-fetches product metadata for every productId on the cart and
|
|
573
|
+
* resolves a display name from the record's `name` (per-locale JSONB) using
|
|
574
|
+
* the `Accept-Language` tag with a `pl` → `pl-PL` → `en-US` → first
|
|
575
|
+
* available fallback chain. Empty list returns an empty map (the
|
|
576
|
+
* serializer treats `null` name as "unknown" and the storefront falls
|
|
577
|
+
* back to the productId).
|
|
578
|
+
*/
|
|
579
|
+
async function loadProductMeta(catalogProducts, productIds, preferredLanguage) {
|
|
580
|
+
const meta = new Map();
|
|
581
|
+
if (productIds.length === 0)
|
|
582
|
+
return meta;
|
|
583
|
+
const unique = Array.from(new Set(productIds));
|
|
584
|
+
const products = await catalogProducts.findByIds(unique);
|
|
585
|
+
for (const p of products) {
|
|
586
|
+
meta.set(p.id, {
|
|
587
|
+
name: pickLocalized(p.name, preferredLanguage),
|
|
588
|
+
slug: p.slug,
|
|
589
|
+
sku: p.sku,
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
return meta;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Walks the locale-fallback chain for a per-locale JSONB field. Tries:
|
|
596
|
+
* 1. exact match on the preferred language (e.g. `pl-PL`)
|
|
597
|
+
* 2. language-only match (`pl` → first `pl-*` key)
|
|
598
|
+
* 3. `en-US`, then `en`
|
|
599
|
+
* 4. first available value
|
|
600
|
+
* 5. empty string
|
|
601
|
+
*/
|
|
602
|
+
function pickLocalized(field, preferredLanguage) {
|
|
603
|
+
if (!field)
|
|
604
|
+
return '';
|
|
605
|
+
if (preferredLanguage && field[preferredLanguage])
|
|
606
|
+
return field[preferredLanguage];
|
|
607
|
+
const langOnly = preferredLanguage?.split('-')[0];
|
|
608
|
+
if (langOnly) {
|
|
609
|
+
const match = Object.keys(field).find((k) => k === langOnly || k.startsWith(`${langOnly}-`));
|
|
610
|
+
if (match)
|
|
611
|
+
return field[match] ?? '';
|
|
612
|
+
}
|
|
613
|
+
if (field['en-US'])
|
|
614
|
+
return field['en-US'];
|
|
615
|
+
if (field['en'])
|
|
616
|
+
return field['en'];
|
|
617
|
+
const first = Object.values(field)[0];
|
|
618
|
+
return first ?? '';
|
|
619
|
+
}
|
|
620
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"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"}
|