@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,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the post-event approval state for a cart whose current
|
|
3
|
+
* approval state is `from`. Pre-flight checks (policy on, cart not
|
|
4
|
+
* empty, buyer-not-org-admin, etc.) live in the service layer; this
|
|
5
|
+
* function captures only the FSM legality of (state, event) pairs.
|
|
6
|
+
*/
|
|
7
|
+
export function nextApprovalState(from, event) {
|
|
8
|
+
if (from === 'rejected_by_org_admin') {
|
|
9
|
+
// Terminal on the approval axis. No event re-opens it; a new cart
|
|
10
|
+
// must be created.
|
|
11
|
+
return { ok: false, reason: 'already_terminal' };
|
|
12
|
+
}
|
|
13
|
+
switch (event) {
|
|
14
|
+
case 'submit':
|
|
15
|
+
if (from === 'not_required')
|
|
16
|
+
return { ok: true, next: 'pending' };
|
|
17
|
+
return { ok: false, reason: 'already_initiated' };
|
|
18
|
+
case 'approve':
|
|
19
|
+
if (from === 'pending')
|
|
20
|
+
return { ok: true, next: 'approved' };
|
|
21
|
+
return { ok: false, reason: 'not_pending' };
|
|
22
|
+
case 'reject':
|
|
23
|
+
if (from === 'pending')
|
|
24
|
+
return { ok: true, next: 'rejected_by_org_admin' };
|
|
25
|
+
return { ok: false, reason: 'not_pending' };
|
|
26
|
+
case 'buyer_mutation':
|
|
27
|
+
// Re-arm rule: any buyer-driven mutation on an `approved` cart
|
|
28
|
+
// silently drops it back to `pending`. Any other state is a no-op
|
|
29
|
+
// (the service short-circuits before calling).
|
|
30
|
+
if (from === 'approved')
|
|
31
|
+
return { ok: true, next: 'pending' };
|
|
32
|
+
return { ok: false, reason: 'not_applicable' };
|
|
33
|
+
case 'policy_off':
|
|
34
|
+
// Turning the org-wide policy off resets every pending/approved
|
|
35
|
+
// cart back to not_required. not_required carts stay put.
|
|
36
|
+
if (from === 'pending' || from === 'approved') {
|
|
37
|
+
return { ok: true, next: 'not_required' };
|
|
38
|
+
}
|
|
39
|
+
return { ok: false, reason: 'not_applicable' };
|
|
40
|
+
case 'policy_on':
|
|
41
|
+
// Turning the policy on does NOT auto-flip existing not_required
|
|
42
|
+
// carts to pending — the buyer must explicitly submit. The hook
|
|
43
|
+
// exists so audit-row builders can decide whether to record a
|
|
44
|
+
// pure policy-change event.
|
|
45
|
+
return { ok: false, reason: 'not_applicable' };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function derivePrimaryCta(input) {
|
|
49
|
+
if (input.status === 'rejected' || input.status === 'completed') {
|
|
50
|
+
return 'blocked_by_organization';
|
|
51
|
+
}
|
|
52
|
+
if (input.approvalStatus === 'pending')
|
|
53
|
+
return 'awaiting_approval';
|
|
54
|
+
if (input.approvalStatus === 'rejected_by_org_admin') {
|
|
55
|
+
return 'blocked_by_organization';
|
|
56
|
+
}
|
|
57
|
+
// `not_required` and `approved` both → checkout. The
|
|
58
|
+
// `submit_for_approval` CTA is only emitted when the Org policy is on
|
|
59
|
+
// and the buyer has not yet submitted.
|
|
60
|
+
if (input.approvalStatus === 'not_required' && input.requiresApproval === true) {
|
|
61
|
+
return 'submit_for_approval';
|
|
62
|
+
}
|
|
63
|
+
return 'checkout';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `true` when an `approved` cart should be silently re-armed to
|
|
67
|
+
* `pending` because the buyer just mutated it. Lives here so the
|
|
68
|
+
* various write paths (add / update / remove / coupon apply / coupon
|
|
69
|
+
* clear) all agree on the same rule.
|
|
70
|
+
*/
|
|
71
|
+
export function shouldReArmOnBuyerMutation(approvalStatus) {
|
|
72
|
+
return approvalStatus === 'approved';
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=cart-state-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cart-state-machine.js","sourceRoot":"","sources":["../../../src/backend/services/cart-state-machine.ts"],"names":[],"mappings":"AAoCA;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAwB,EACxB,KAAoB;IAEpB,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;QACrC,kEAAkE;QAClE,mBAAmB;QACnB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACnD,CAAC;IAED,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,QAAQ;YACX,IAAI,IAAI,KAAK,cAAc;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAClE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAEpD,KAAK,SAAS;YACZ,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;YAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QAE9C,KAAK,QAAQ;YACX,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;YAC3E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;QAE9C,KAAK,gBAAgB;YACnB,+DAA+D;YAC/D,kEAAkE;YAClE,+CAA+C;YAC/C,IAAI,IAAI,KAAK,UAAU;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAEjD,KAAK,YAAY;YACf,gEAAgE;YAChE,0DAA0D;YAC1D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC9C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;YAC5C,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAEjD,KAAK,WAAW;YACd,iEAAiE;YACjE,gEAAgE;YAChE,8DAA8D;YAC9D,4BAA4B;YAC5B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACnD,CAAC;AACH,CAAC;AAaD,MAAM,UAAU,gBAAgB,CAAC,KAIhC;IACC,IAAI,KAAK,CAAC,MAAM,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAChE,OAAO,yBAAyB,CAAC;IACnC,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IACnE,IAAI,KAAK,CAAC,cAAc,KAAK,uBAAuB,EAAE,CAAC;QACrD,OAAO,yBAAyB,CAAC;IACnC,CAAC;IACD,qDAAqD;IACrD,sEAAsE;IACtE,uCAAuC;IACvC,IAAI,KAAK,CAAC,cAAc,KAAK,cAAc,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC/E,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,cAAkC;IAElC,OAAO,cAAc,KAAK,UAAU,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type CatalogProductReadPort, type ProductAudience } from '@endora-commerce/contracts';
|
|
3
|
+
/**
|
|
4
|
+
* Cart up-sell aggregator (feature 027 §R9 / FR-039).
|
|
5
|
+
*
|
|
6
|
+
* Walks `product_links` (kind = `up_sell`) for every product currently in
|
|
7
|
+
* the cart, aggregates by target product, drops candidates that are
|
|
8
|
+
* already in the cart, and ranks by match-count (most-linked-from first),
|
|
9
|
+
* tie-breaking by older `created_at`.
|
|
10
|
+
*
|
|
11
|
+
* The catalog module's `ProductLink.kind = 'up_sell'` is the source the
|
|
12
|
+
* spec calls "Up-sell" (the same relation surfaced on PDP). The strip's
|
|
13
|
+
* placement is the cart UI; the relation type is the same.
|
|
14
|
+
*/
|
|
15
|
+
export interface CartUpsellCandidate {
|
|
16
|
+
productId: string;
|
|
17
|
+
productName: string;
|
|
18
|
+
productSlug: string;
|
|
19
|
+
productThumbnailUrl: string | null;
|
|
20
|
+
matchCount: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class CartUpsellService {
|
|
23
|
+
private readonly emFactory;
|
|
24
|
+
/**
|
|
25
|
+
* `catalog`'s product read model (feature 075, Phase C). Both reads this
|
|
26
|
+
* replaced — the `up_sell` links and the target rows — were `em.find`
|
|
27
|
+
* against `catalog`'s tables, so a switched-off `catalog` still filled the
|
|
28
|
+
* cart's up-sell strip with products the platform had stopped serving.
|
|
29
|
+
*/
|
|
30
|
+
private readonly catalogProducts;
|
|
31
|
+
constructor(emFactory: () => EntityManager,
|
|
32
|
+
/**
|
|
33
|
+
* `catalog`'s product read model (feature 075, Phase C). Both reads this
|
|
34
|
+
* replaced — the `up_sell` links and the target rows — were `em.find`
|
|
35
|
+
* against `catalog`'s tables, so a switched-off `catalog` still filled the
|
|
36
|
+
* cart's up-sell strip with products the platform had stopped serving.
|
|
37
|
+
*/
|
|
38
|
+
catalogProducts: CatalogProductReadPort);
|
|
39
|
+
forCart(cartId: string, limit: number,
|
|
40
|
+
/**
|
|
41
|
+
* Who is looking at the strip (issue #227). Required: this method returns
|
|
42
|
+
* a product's name, slug and thumbnail for rows the buyer never asked for
|
|
43
|
+
* by id, which makes it a listing rather than a lookup, and a listing that
|
|
44
|
+
* defaulted its audience would silently show the wrong one.
|
|
45
|
+
*/
|
|
46
|
+
audience: ProductAudience): Promise<CartUpsellCandidate[]>;
|
|
47
|
+
private thumbnailFor;
|
|
48
|
+
/**
|
|
49
|
+
* Picks the most useful locale from the multilingual `name` JSONB. The
|
|
50
|
+
* cart UI hands the buyer's session locale to the API; the resolver in
|
|
51
|
+
* front of this service is expected to use that. For now we fall
|
|
52
|
+
* through to the first available locale, matching the foundation's
|
|
53
|
+
* "best-effort" rendering pattern.
|
|
54
|
+
*/
|
|
55
|
+
private localizedName;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=cart-upsell-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cart-upsell-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/cart-upsell-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAEL,KAAK,sBAAsB,EAE3B,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAGpC;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe;gBAPf,SAAS,EAAE,MAAM,aAAa;IAC/C;;;;;OAKG;IACc,eAAe,EAAE,sBAAsB;IAGpD,OAAO,CACX,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM;IACb;;;;;OAKG;IACH,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IA4DjC,OAAO,CAAC,YAAY;IAUpB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;CAOtB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { isProductVisibleTo, } from '@endora-commerce/contracts';
|
|
2
|
+
import { CartItem } from '../entities/cart-item.entity.js';
|
|
3
|
+
export class CartUpsellService {
|
|
4
|
+
emFactory;
|
|
5
|
+
catalogProducts;
|
|
6
|
+
constructor(emFactory,
|
|
7
|
+
/**
|
|
8
|
+
* `catalog`'s product read model (feature 075, Phase C). Both reads this
|
|
9
|
+
* replaced — the `up_sell` links and the target rows — were `em.find`
|
|
10
|
+
* against `catalog`'s tables, so a switched-off `catalog` still filled the
|
|
11
|
+
* cart's up-sell strip with products the platform had stopped serving.
|
|
12
|
+
*/
|
|
13
|
+
catalogProducts) {
|
|
14
|
+
this.emFactory = emFactory;
|
|
15
|
+
this.catalogProducts = catalogProducts;
|
|
16
|
+
}
|
|
17
|
+
async forCart(cartId, limit,
|
|
18
|
+
/**
|
|
19
|
+
* Who is looking at the strip (issue #227). Required: this method returns
|
|
20
|
+
* a product's name, slug and thumbnail for rows the buyer never asked for
|
|
21
|
+
* by id, which makes it a listing rather than a lookup, and a listing that
|
|
22
|
+
* defaulted its audience would silently show the wrong one.
|
|
23
|
+
*/
|
|
24
|
+
audience) {
|
|
25
|
+
if (limit <= 0)
|
|
26
|
+
return [];
|
|
27
|
+
const em = this.emFactory();
|
|
28
|
+
const items = await em.find(CartItem, { cartId });
|
|
29
|
+
if (items.length === 0)
|
|
30
|
+
return [];
|
|
31
|
+
const sourceProductIds = Array.from(new Set(items.map((it) => it.productId)));
|
|
32
|
+
// One query: every up_sell link emanating from any of the source
|
|
33
|
+
// products. The target set is deduplicated in app code so we can
|
|
34
|
+
// attach match-counts cheaply.
|
|
35
|
+
const links = await this.catalogProducts.listLinksBySourceIds(sourceProductIds, 'up_sell');
|
|
36
|
+
if (links.length === 0)
|
|
37
|
+
return [];
|
|
38
|
+
const matchCountByTarget = new Map();
|
|
39
|
+
for (const link of links) {
|
|
40
|
+
// Exclude products already in the cart.
|
|
41
|
+
if (sourceProductIds.includes(link.targetProductId))
|
|
42
|
+
continue;
|
|
43
|
+
matchCountByTarget.set(link.targetProductId, (matchCountByTarget.get(link.targetProductId) ?? 0) + 1);
|
|
44
|
+
}
|
|
45
|
+
if (matchCountByTarget.size === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const targetIds = Array.from(matchCountByTarget.keys());
|
|
48
|
+
// Issue #227 — an up-sell target is a product the buyer never named, so
|
|
49
|
+
// the strip is a listing. A link from a product they may see to one they
|
|
50
|
+
// may not does not make the second one theirs to see.
|
|
51
|
+
const products = (await this.catalogProducts.findByIds(targetIds)).filter((p) => isProductVisibleTo(p, audience));
|
|
52
|
+
const byId = new Map(products.map((p) => [p.id, p]));
|
|
53
|
+
const candidates = targetIds
|
|
54
|
+
.map((id) => byId.get(id))
|
|
55
|
+
.filter((p) => Boolean(p))
|
|
56
|
+
.map((p) => ({
|
|
57
|
+
productId: p.id,
|
|
58
|
+
productName: this.localizedName(p),
|
|
59
|
+
productSlug: p.slug,
|
|
60
|
+
productThumbnailUrl: this.thumbnailFor(p),
|
|
61
|
+
matchCount: matchCountByTarget.get(p.id) ?? 0,
|
|
62
|
+
}));
|
|
63
|
+
candidates.sort((a, b) => {
|
|
64
|
+
if (b.matchCount !== a.matchCount)
|
|
65
|
+
return b.matchCount - a.matchCount;
|
|
66
|
+
// Tie-break by older `created_at` (the existing product wins over
|
|
67
|
+
// a newly-added one). Sorted post-mapping for clarity.
|
|
68
|
+
const aProduct = byId.get(a.productId);
|
|
69
|
+
const bProduct = byId.get(b.productId);
|
|
70
|
+
const aTime = aProduct?.createdAt?.getTime() ?? 0;
|
|
71
|
+
const bTime = bProduct?.createdAt?.getTime() ?? 0;
|
|
72
|
+
return aTime - bTime;
|
|
73
|
+
});
|
|
74
|
+
return candidates.slice(0, limit);
|
|
75
|
+
}
|
|
76
|
+
thumbnailFor(product) {
|
|
77
|
+
// The product record carries the `attributeValues` JSONB blob that
|
|
78
|
+
// foundation code reads for `primaryAssetUrl`. We mirror that read
|
|
79
|
+
// path; if missing, the caller's UI hides the thumbnail.
|
|
80
|
+
const url = product.attributeValues?.['primaryAssetUrl'];
|
|
81
|
+
return typeof url === 'string' && url.length > 0 ? url : null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Picks the most useful locale from the multilingual `name` JSONB. The
|
|
85
|
+
* cart UI hands the buyer's session locale to the API; the resolver in
|
|
86
|
+
* front of this service is expected to use that. For now we fall
|
|
87
|
+
* through to the first available locale, matching the foundation's
|
|
88
|
+
* "best-effort" rendering pattern.
|
|
89
|
+
*/
|
|
90
|
+
localizedName(product) {
|
|
91
|
+
if (!product.name || typeof product.name !== 'object')
|
|
92
|
+
return product.id;
|
|
93
|
+
const en = product.name['en-US'] ?? product.name['en'];
|
|
94
|
+
if (typeof en === 'string' && en.length > 0)
|
|
95
|
+
return en;
|
|
96
|
+
const first = Object.values(product.name).find((v) => typeof v === 'string' && v.length > 0);
|
|
97
|
+
return first ?? product.id;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=cart-upsell-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cart-upsell-service.js","sourceRoot":"","sources":["../../../src/backend/services/cart-upsell-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CustomerAccountReadPort, OrganizationDetailsPort } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The effective customer group of a cart's owner (issue #177).
|
|
4
|
+
*
|
|
5
|
+
* The chain is the platform's one answer to "which group is this buyer in":
|
|
6
|
+
* the account's own group, else the Organization's, else none. The pricing
|
|
7
|
+
* engine spells it as `context.customerGroupId ?? organization.customerGroupId`
|
|
8
|
+
* and `pwa`'s push audience builder as `account.customerGroupId ?? orgGroup`;
|
|
9
|
+
* this is the same rule for the promotion engine, which had been receiving a
|
|
10
|
+
* hard-coded `null` on every cart path since the field was introduced.
|
|
11
|
+
*
|
|
12
|
+
* Both rows arrive over their owners' published ports, so a switched-off
|
|
13
|
+
* `customer_accounts` or `organizations` refuses here rather than answering
|
|
14
|
+
* "no group" — which reads as a legitimate answer and is not one: it silently
|
|
15
|
+
* withdraws every group-targeted discount from every buyer.
|
|
16
|
+
*
|
|
17
|
+
* An anonymous cart has neither an account nor an Organization and resolves to
|
|
18
|
+
* `null`, which is the correct answer rather than a degraded one.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveCartCustomerGroupId(ports: {
|
|
21
|
+
customerAccounts: CustomerAccountReadPort;
|
|
22
|
+
organizations: OrganizationDetailsPort;
|
|
23
|
+
}, owner: {
|
|
24
|
+
customerAccountId: string | null;
|
|
25
|
+
organizationId: string | null;
|
|
26
|
+
}): Promise<string | null>;
|
|
27
|
+
//# sourceMappingURL=customer-group-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customer-group-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/customer-group-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAEnG;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE;IACL,gBAAgB,EAAE,uBAAuB,CAAC;IAC1C,aAAa,EAAE,uBAAuB,CAAC;CACxC,EACD,KAAK,EAAE;IAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACzE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUxB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The effective customer group of a cart's owner (issue #177).
|
|
3
|
+
*
|
|
4
|
+
* The chain is the platform's one answer to "which group is this buyer in":
|
|
5
|
+
* the account's own group, else the Organization's, else none. The pricing
|
|
6
|
+
* engine spells it as `context.customerGroupId ?? organization.customerGroupId`
|
|
7
|
+
* and `pwa`'s push audience builder as `account.customerGroupId ?? orgGroup`;
|
|
8
|
+
* this is the same rule for the promotion engine, which had been receiving a
|
|
9
|
+
* hard-coded `null` on every cart path since the field was introduced.
|
|
10
|
+
*
|
|
11
|
+
* Both rows arrive over their owners' published ports, so a switched-off
|
|
12
|
+
* `customer_accounts` or `organizations` refuses here rather than answering
|
|
13
|
+
* "no group" — which reads as a legitimate answer and is not one: it silently
|
|
14
|
+
* withdraws every group-targeted discount from every buyer.
|
|
15
|
+
*
|
|
16
|
+
* An anonymous cart has neither an account nor an Organization and resolves to
|
|
17
|
+
* `null`, which is the correct answer rather than a degraded one.
|
|
18
|
+
*/
|
|
19
|
+
export async function resolveCartCustomerGroupId(ports, owner) {
|
|
20
|
+
if (owner.customerAccountId) {
|
|
21
|
+
const account = await ports.customerAccounts.findById(owner.customerAccountId);
|
|
22
|
+
if (account?.customerGroupId)
|
|
23
|
+
return account.customerGroupId;
|
|
24
|
+
}
|
|
25
|
+
if (owner.organizationId) {
|
|
26
|
+
const organization = await ports.organizations.findById(owner.organizationId);
|
|
27
|
+
if (organization?.customerGroupId)
|
|
28
|
+
return organization.customerGroupId;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=customer-group-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customer-group-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/customer-group-resolver.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAGC,EACD,KAA0E;IAE1E,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAC/E,IAAI,OAAO,EAAE,eAAe;YAAE,OAAO,OAAO,CAAC,eAAe,CAAC;IAC/D,CAAC;IACD,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QACzB,MAAM,YAAY,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAC9E,IAAI,YAAY,EAAE,eAAe;YAAE,OAAO,YAAY,CAAC,eAAe,CAAC;IACzE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { type ModuleCliCommand } from '@endora-commerce/contracts';
|
|
2
|
+
import type { ModuleContext } from '@endora-commerce/platform/kernel';
|
|
3
|
+
/**
|
|
4
|
+
* Carts module — feature 027 consolidation pass.
|
|
5
|
+
*
|
|
6
|
+
* Settings (`carts.abandonment.*`) declared here are seeded by the module-
|
|
7
|
+
* lifecycle ManifestReconciler on backend boot. No raw INSERT in the
|
|
8
|
+
* migration; the reconciler is the single seed path (matches the
|
|
9
|
+
* `inventory.*` and `organizations.*` patterns).
|
|
10
|
+
*/
|
|
11
|
+
export declare const CARTS_SETTING_CODES: {
|
|
12
|
+
readonly ABANDONMENT_INACTIVITY_MINUTES: "carts.abandonment.inactivity_minutes";
|
|
13
|
+
readonly ABANDONMENT_NOTIFICATION_RECIPIENT: "carts.abandonment.notification_recipient";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The manifest defaults, exported so a read that has to degrade degrades to
|
|
17
|
+
* *this* binding rather than to a literal invented at the call site (feature
|
|
18
|
+
* 072, D-43).
|
|
19
|
+
*
|
|
20
|
+
* The two used to diverge: the manifest said 10080 while both readers' `catch`
|
|
21
|
+
* answered `0`, and `CartAbandonmentWorker` treats `<= 0` as "sweep nothing".
|
|
22
|
+
* The divergence did not merely lose the configured value — it inverted the
|
|
23
|
+
* feature, and it would still have been off after the channel was fixed. A
|
|
24
|
+
* compiled-in fallback is a second source of truth consulted only when the
|
|
25
|
+
* first is unreachable, i.e. exactly when nobody is watching.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_ABANDONMENT_INACTIVITY_MINUTES = 10080;
|
|
28
|
+
export declare const DEFAULT_ABANDONMENT_NOTIFICATION_RECIPIENT = "";
|
|
29
|
+
export declare const manifest: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
version: string;
|
|
33
|
+
dependencies: string[];
|
|
34
|
+
description?: string | undefined;
|
|
35
|
+
acknowledgedDependencies?: {
|
|
36
|
+
moduleId: string;
|
|
37
|
+
port: string;
|
|
38
|
+
reason: string;
|
|
39
|
+
}[] | undefined;
|
|
40
|
+
nonBindingDependencies?: {
|
|
41
|
+
moduleId: string;
|
|
42
|
+
name: string;
|
|
43
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
44
|
+
reason: string;
|
|
45
|
+
whenAbsent?: string | undefined;
|
|
46
|
+
}[] | undefined;
|
|
47
|
+
activation?: {
|
|
48
|
+
settingCode: string;
|
|
49
|
+
default: boolean;
|
|
50
|
+
} | {
|
|
51
|
+
nonDeactivatable: true;
|
|
52
|
+
reason: string;
|
|
53
|
+
} | undefined;
|
|
54
|
+
settings?: {
|
|
55
|
+
moduleCode: string;
|
|
56
|
+
groups: {
|
|
57
|
+
code: string;
|
|
58
|
+
name: string;
|
|
59
|
+
salesChannelCodes?: string[] | undefined;
|
|
60
|
+
isSystemProtected?: boolean | undefined;
|
|
61
|
+
}[];
|
|
62
|
+
settings: {
|
|
63
|
+
code: string;
|
|
64
|
+
name: string;
|
|
65
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
66
|
+
defaultValue: unknown;
|
|
67
|
+
description?: string | undefined;
|
|
68
|
+
groupCode?: string | undefined;
|
|
69
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
70
|
+
salesChannelCodes?: string[] | undefined;
|
|
71
|
+
enumOptions?: string[] | undefined;
|
|
72
|
+
configurationType?: string | undefined;
|
|
73
|
+
hidden?: boolean | undefined;
|
|
74
|
+
}[];
|
|
75
|
+
} | undefined;
|
|
76
|
+
i18n?: {
|
|
77
|
+
bundlesDir: string;
|
|
78
|
+
} | undefined;
|
|
79
|
+
docs?: false | {
|
|
80
|
+
dir: string;
|
|
81
|
+
} | undefined;
|
|
82
|
+
demo?: false | {
|
|
83
|
+
summary: string;
|
|
84
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
85
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
86
|
+
after?: readonly string[] | undefined;
|
|
87
|
+
package?: string | undefined;
|
|
88
|
+
} | undefined;
|
|
89
|
+
actions?: {
|
|
90
|
+
id: string;
|
|
91
|
+
labelKey: string;
|
|
92
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
93
|
+
targetRoute: string;
|
|
94
|
+
keywords: string[];
|
|
95
|
+
weight: number;
|
|
96
|
+
descriptionKey?: string | undefined;
|
|
97
|
+
requiredPermission?: string | undefined;
|
|
98
|
+
}[] | undefined;
|
|
99
|
+
permissions?: {
|
|
100
|
+
code: string;
|
|
101
|
+
label: string;
|
|
102
|
+
module?: string | undefined;
|
|
103
|
+
description?: string | undefined;
|
|
104
|
+
requires?: string[] | undefined;
|
|
105
|
+
}[] | undefined;
|
|
106
|
+
transactionalEmails?: {
|
|
107
|
+
code: string;
|
|
108
|
+
name: string;
|
|
109
|
+
variables: {
|
|
110
|
+
key: string;
|
|
111
|
+
label: string;
|
|
112
|
+
sampleValue?: string | undefined;
|
|
113
|
+
description?: string | undefined;
|
|
114
|
+
}[];
|
|
115
|
+
description?: string | undefined;
|
|
116
|
+
group?: string | undefined;
|
|
117
|
+
}[] | undefined;
|
|
118
|
+
capabilities?: string[] | undefined;
|
|
119
|
+
exclusiveCapabilities?: {
|
|
120
|
+
key: string;
|
|
121
|
+
errorCode: string;
|
|
122
|
+
}[] | undefined;
|
|
123
|
+
errorCodes?: {
|
|
124
|
+
code: string;
|
|
125
|
+
tokens?: string[] | undefined;
|
|
126
|
+
}[] | undefined;
|
|
127
|
+
blocks?: {
|
|
128
|
+
name: string;
|
|
129
|
+
labelKey: string;
|
|
130
|
+
category: string;
|
|
131
|
+
contexts: ("email" | "invoice" | "cms" | "newsletter")[];
|
|
132
|
+
fields: Record<string, {
|
|
133
|
+
type: "number" | "object" | "array" | "uuid" | "text" | "select" | "textarea" | "radio" | "external" | "richtext";
|
|
134
|
+
label?: string | undefined;
|
|
135
|
+
required?: boolean | undefined;
|
|
136
|
+
options?: {
|
|
137
|
+
label: string;
|
|
138
|
+
value: string | number;
|
|
139
|
+
}[] | undefined;
|
|
140
|
+
refKind?: string | undefined;
|
|
141
|
+
}>;
|
|
142
|
+
descriptionKey?: string | undefined;
|
|
143
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
144
|
+
responsiveFields?: string[] | undefined;
|
|
145
|
+
previewIcon?: string | undefined;
|
|
146
|
+
weight?: number | undefined;
|
|
147
|
+
}[] | undefined;
|
|
148
|
+
blockCategories?: {
|
|
149
|
+
key: string;
|
|
150
|
+
titleKey: string;
|
|
151
|
+
contexts: ("email" | "invoice" | "cms" | "newsletter")[];
|
|
152
|
+
weight?: number | undefined;
|
|
153
|
+
visible?: boolean | undefined;
|
|
154
|
+
}[] | undefined;
|
|
155
|
+
env?: {
|
|
156
|
+
name: string;
|
|
157
|
+
describes: {
|
|
158
|
+
en: string;
|
|
159
|
+
pl: string;
|
|
160
|
+
};
|
|
161
|
+
requirement: {
|
|
162
|
+
kind: "required";
|
|
163
|
+
} | {
|
|
164
|
+
kind: "requiredWhen";
|
|
165
|
+
input: string;
|
|
166
|
+
equals: string;
|
|
167
|
+
} | {
|
|
168
|
+
kind: "optional";
|
|
169
|
+
without: {
|
|
170
|
+
en: string;
|
|
171
|
+
pl: string;
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
secret: boolean;
|
|
175
|
+
generable: boolean;
|
|
176
|
+
owner: {
|
|
177
|
+
kind: "platform";
|
|
178
|
+
} | {
|
|
179
|
+
kind: "application";
|
|
180
|
+
application: "admin" | "backend" | "storefront";
|
|
181
|
+
} | {
|
|
182
|
+
kind: "module";
|
|
183
|
+
moduleId: string;
|
|
184
|
+
};
|
|
185
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
186
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
187
|
+
}[] | undefined;
|
|
188
|
+
};
|
|
189
|
+
/** Legacy export retained for backward compatibility. */
|
|
190
|
+
export declare const cartsManifest: {
|
|
191
|
+
moduleCode: string;
|
|
192
|
+
groups: {
|
|
193
|
+
code: string;
|
|
194
|
+
name: string;
|
|
195
|
+
salesChannelCodes?: string[] | undefined;
|
|
196
|
+
isSystemProtected?: boolean | undefined;
|
|
197
|
+
}[];
|
|
198
|
+
settings: {
|
|
199
|
+
code: string;
|
|
200
|
+
name: string;
|
|
201
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
202
|
+
defaultValue: unknown;
|
|
203
|
+
description?: string | undefined;
|
|
204
|
+
groupCode?: string | undefined;
|
|
205
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
206
|
+
salesChannelCodes?: string[] | undefined;
|
|
207
|
+
enumOptions?: string[] | undefined;
|
|
208
|
+
configurationType?: string | undefined;
|
|
209
|
+
hidden?: boolean | undefined;
|
|
210
|
+
}[];
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* The operator command this module declares — feature 080, T042b / D-160.9.
|
|
214
|
+
*
|
|
215
|
+
* It was `scripts/abandonment-sweep.ts`, the tree's one call site of
|
|
216
|
+
* `requireModuleEnabled` and the worked example D-157 was written around. The
|
|
217
|
+
* host asks that question now, at the declaration seam, for every command; see
|
|
218
|
+
* `cli/abandonment-sweep.ts` for what the move changed and what it did not.
|
|
219
|
+
*/
|
|
220
|
+
export declare const cliCommands: ReadonlyArray<ModuleCliCommand<ModuleContext>>;
|
|
221
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE;;;;;;;GAOG;AAEH,eAAO,MAAM,mBAAmB;;;CAGtB,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sCAAsC,QAAQ,CAAC;AAC5D,eAAO,MAAM,0CAA0C,KAAK,CAAC;AA2B7D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+JnB,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;CAAW,CAAC;AAEtC;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAOtE,CAAC"}
|