@endora-commerce/mod-comparisons 0.0.0-stage → 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 +61 -2
- package/dist/admin/api/comparisons-client.d.ts +24 -0
- package/dist/admin/api/comparisons-client.d.ts.map +1 -0
- package/dist/admin/api/comparisons-client.js +15 -0
- package/dist/admin/api/comparisons-client.js.map +1 -0
- package/dist/admin/index.d.ts +37 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +49 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/ComparisonDetailPage.d.ts +19 -0
- package/dist/admin/pages/ComparisonDetailPage.d.ts.map +1 -0
- package/dist/admin/pages/ComparisonDetailPage.js +62 -0
- package/dist/admin/pages/ComparisonDetailPage.js.map +1 -0
- package/dist/admin/pages/ComparisonsListPage.d.ts +10 -0
- package/dist/admin/pages/ComparisonsListPage.d.ts.map +1 -0
- package/dist/admin/pages/ComparisonsListPage.js +71 -0
- package/dist/admin/pages/ComparisonsListPage.js.map +1 -0
- package/dist/backend/entities/comparison-product.entity.d.ts +22 -0
- package/dist/backend/entities/comparison-product.entity.d.ts.map +1 -0
- package/dist/backend/entities/comparison-product.entity.js +54 -0
- package/dist/backend/entities/comparison-product.entity.js.map +1 -0
- package/dist/backend/entities/comparison.entity.d.ts +61 -0
- package/dist/backend/entities/comparison.entity.d.ts.map +1 -0
- package/dist/backend/entities/comparison.entity.js +115 -0
- package/dist/backend/entities/comparison.entity.js.map +1 -0
- package/dist/backend/index.d.ts +61 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +117 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/request-actor.d.ts +22 -0
- package/dist/backend/request-actor.d.ts.map +1 -0
- package/dist/backend/request-actor.js +24 -0
- package/dist/backend/request-actor.js.map +1 -0
- package/dist/backend/routes.admin.d.ts +22 -0
- package/dist/backend/routes.admin.d.ts.map +1 -0
- package/dist/backend/routes.admin.js +41 -0
- package/dist/backend/routes.admin.js.map +1 -0
- package/dist/backend/routes.public.d.ts +33 -0
- package/dist/backend/routes.public.d.ts.map +1 -0
- package/dist/backend/routes.public.js +215 -0
- package/dist/backend/routes.public.js.map +1 -0
- package/dist/backend/routes.share.d.ts +27 -0
- package/dist/backend/routes.share.d.ts.map +1 -0
- package/dist/backend/routes.share.js +49 -0
- package/dist/backend/routes.share.js.map +1 -0
- package/dist/backend/services/anonymous-token-cookie.d.ts +23 -0
- package/dist/backend/services/anonymous-token-cookie.d.ts.map +1 -0
- package/dist/backend/services/anonymous-token-cookie.js +33 -0
- package/dist/backend/services/anonymous-token-cookie.js.map +1 -0
- package/dist/backend/services/asset-byte-fetcher.d.ts +37 -0
- package/dist/backend/services/asset-byte-fetcher.d.ts.map +1 -0
- package/dist/backend/services/asset-byte-fetcher.js +70 -0
- package/dist/backend/services/asset-byte-fetcher.js.map +1 -0
- package/dist/backend/services/comparable-attribute-projection.d.ts +57 -0
- package/dist/backend/services/comparable-attribute-projection.d.ts.map +1 -0
- package/dist/backend/services/comparable-attribute-projection.js +136 -0
- package/dist/backend/services/comparable-attribute-projection.js.map +1 -0
- package/dist/backend/services/comparison-admin.service.d.ts +31 -0
- package/dist/backend/services/comparison-admin.service.d.ts.map +1 -0
- package/dist/backend/services/comparison-admin.service.js +184 -0
- package/dist/backend/services/comparison-admin.service.js.map +1 -0
- package/dist/backend/services/comparison-pdf-renderer.d.ts +57 -0
- package/dist/backend/services/comparison-pdf-renderer.d.ts.map +1 -0
- package/dist/backend/services/comparison-pdf-renderer.js +211 -0
- package/dist/backend/services/comparison-pdf-renderer.js.map +1 -0
- package/dist/backend/services/comparison-service.d.ts +339 -0
- package/dist/backend/services/comparison-service.d.ts.map +1 -0
- package/dist/backend/services/comparison-service.js +588 -0
- package/dist/backend/services/comparison-service.js.map +1 -0
- package/dist/backend/services/share-token-generator.d.ts +20 -0
- package/dist/backend/services/share-token-generator.d.ts.map +1 -0
- package/dist/backend/services/share-token-generator.js +23 -0
- package/dist/backend/services/share-token-generator.js.map +1 -0
- package/dist/manifest.d.ts +199 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +179 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260501T185834_comparisons_init.d.ts +32 -0
- package/dist/migrations/20260501T185834_comparisons_init.d.ts.map +1 -0
- package/dist/migrations/20260501T185834_comparisons_init.js +102 -0
- package/dist/migrations/20260501T185834_comparisons_init.js.map +1 -0
- package/dist/migrations/20260830T163143_comparisons_organization_attribution.d.ts +84 -0
- package/dist/migrations/20260830T163143_comparisons_organization_attribution.d.ts.map +1 -0
- package/dist/migrations/20260830T163143_comparisons_organization_attribution.js +147 -0
- package/dist/migrations/20260830T163143_comparisons_organization_attribution.js.map +1 -0
- package/dist/migrations/index.d.ts +28 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +31 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/comparisons.md +278 -0
- package/i18n/en.json +51 -0
- package/i18n/pl.json +51 -0
- package/package.json +102 -3
- package/tailwind.css +14 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-pdf-renderer.js","sourceRoot":"","sources":["../../../src/backend/services/comparison-pdf-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import { type CatalogAttributeReadPort, type CatalogGalleryPort, type CatalogProductReadPort, type ComparisonOwnerView, type ComparisonDisplayMode, type CustomerAccountReadPort, type ListingPricePort, type OrganizationDetailsPort, type ProductAudience } from '@endora-commerce/contracts';
|
|
3
|
+
import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
|
|
4
|
+
import type { SettingsReadPort } from '@endora-commerce/platform/kernel';
|
|
5
|
+
import { Comparison } from '../entities/comparison.entity.js';
|
|
6
|
+
import type { ShareTokenGenerator } from './share-token-generator.js';
|
|
7
|
+
import type { ComparableAttributeProjection } from './comparable-attribute-projection.js';
|
|
8
|
+
/**
|
|
9
|
+
* ComparisonService — feature 007 / T023 + T024.
|
|
10
|
+
*
|
|
11
|
+
* Stateful service owning the Comparison resource. Every cross-module read
|
|
12
|
+
* goes through a published port — {@link CatalogProductReadPort},
|
|
13
|
+
* {@link CatalogAttributeReadPort}, {@link ListingPricePort} and
|
|
14
|
+
* {@link SettingsReadPort}. The products a comparison holds are `catalog`'s
|
|
15
|
+
* rows, not this module's: reading them with `em.find(Product, …)` was a query
|
|
16
|
+
* nothing could gate, so a comparison kept resolving names and availability
|
|
17
|
+
* out of a module an operator had switched off (feature 075, Phase C).
|
|
18
|
+
*
|
|
19
|
+
* Behaviour summary (per `data-model.md`, `research.md`, and
|
|
20
|
+
* `contracts/public-comparisons-crud.md`):
|
|
21
|
+
*
|
|
22
|
+
* - `getForOwner` returns the caller's live Comparison or `null`. No
|
|
23
|
+
* side effects.
|
|
24
|
+
* - `addProduct` enforces `compare.max_products` on add (R-7). Throws
|
|
25
|
+
* {@link ComparisonFullError} when the cap would be exceeded.
|
|
26
|
+
* Idempotent on duplicates: re-adding a product already in the set
|
|
27
|
+
* is a no-op that returns the current comparison.
|
|
28
|
+
* - `removeProduct` throws {@link ProductNotInComparisonError} when
|
|
29
|
+
* the product is not in the set; emptying the set is allowed.
|
|
30
|
+
* - `setDisplayMode` only updates the persisted mode; refuses with
|
|
31
|
+
* {@link ComparisonNotFoundError} when the caller has no comparison.
|
|
32
|
+
* - `deleteForOwner` hard-deletes; cascades onto `comparison_products`
|
|
33
|
+
* by FK. Invalidates the share token (the row is gone — the share
|
|
34
|
+
* endpoint will return 404).
|
|
35
|
+
* - `findByShareToken` resolves the token in O(1) via the UNIQUE
|
|
36
|
+
* index. Used in US2 for the recipient view.
|
|
37
|
+
* - `buildOwnerView` produces the on-the-wire shape for `GET /me`
|
|
38
|
+
* (and is reused for the share-token recipient view in US2 and the
|
|
39
|
+
* admin detail view in US5).
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Who is reading a comparison.
|
|
43
|
+
*
|
|
44
|
+
* Two kinds rather than one `ProductAudience`, because an administrator is not
|
|
45
|
+
* a buyer and there is no audience that means "may see everything": the
|
|
46
|
+
* predicate in {@link isProductVisibleTo} answers for a *shopper*, and every
|
|
47
|
+
* audience an administrator could be given would drop the organisation-
|
|
48
|
+
* restricted rows the audit screen exists to show. Making that an explicit
|
|
49
|
+
* kind is what keeps it from being expressed as a permissive audience
|
|
50
|
+
* somebody later reuses on a storefront path.
|
|
51
|
+
*/
|
|
52
|
+
export type ComparisonViewer = {
|
|
53
|
+
readonly kind: 'buyer';
|
|
54
|
+
readonly audience: ProductAudience;
|
|
55
|
+
} | {
|
|
56
|
+
readonly kind: 'administrator';
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* The most restrictive viewer there is — the public. The right default for a
|
|
60
|
+
* caller that has not been taught to resolve its reader, for the same reason
|
|
61
|
+
* {@link ANONYMOUS_PRODUCT_AUDIENCE} is: everything it may see, every other
|
|
62
|
+
* viewer may see too.
|
|
63
|
+
*/
|
|
64
|
+
export declare const ANONYMOUS_COMPARISON_VIEWER: ComparisonViewer;
|
|
65
|
+
export declare class ComparisonService {
|
|
66
|
+
#private;
|
|
67
|
+
private readonly emFactory;
|
|
68
|
+
private readonly projection;
|
|
69
|
+
private readonly tokens;
|
|
70
|
+
/**
|
|
71
|
+
* `catalog`'s product read model. Not optional: a comparison is a list of
|
|
72
|
+
* products, so a service that cannot read one has nothing to answer with —
|
|
73
|
+
* where the settings and pricing arguments below have a defined fallback,
|
|
74
|
+
* this has none.
|
|
75
|
+
*/
|
|
76
|
+
private readonly catalogProducts;
|
|
77
|
+
/**
|
|
78
|
+
* The sanctioned bridge accessor (Constitution XII), for the assortment
|
|
79
|
+
* gate `addProduct` owes (issue #259). Required for the same reason
|
|
80
|
+
* `catalogProducts` is: an optional one would spell "this rig wired no
|
|
81
|
+
* membership service" and "this channel publishes the product" as the same
|
|
82
|
+
* answer.
|
|
83
|
+
*/
|
|
84
|
+
private readonly channelMembership;
|
|
85
|
+
/**
|
|
86
|
+
* Feature 075 / D-87 — `catalog`'s gallery read model, for the base image
|
|
87
|
+
* of each column. Required for the same reason `catalogProducts` is: the
|
|
88
|
+
* alternative to a wired port is not "a comparison without pictures", it is
|
|
89
|
+
* a service that cannot tell an operator who set no base image from a rig
|
|
90
|
+
* that wired no owner.
|
|
91
|
+
*
|
|
92
|
+
* `baseImageUrls`, not `list`: the read is a batch, it must tolerate an id
|
|
93
|
+
* whose product has since been removed, and the value a column renders is a
|
|
94
|
+
* **url**, which `catalog` resolves through the asset port it holds. Those
|
|
95
|
+
* three are exactly why the single join this replaced could not be swapped
|
|
96
|
+
* for the per-product port that already existed.
|
|
97
|
+
*/
|
|
98
|
+
private readonly catalogGallery;
|
|
99
|
+
/**
|
|
100
|
+
* Optional settings service. When undefined, `compare.max_products`
|
|
101
|
+
* defaults to {@link DEFAULT_COMPARE_MAX_PRODUCTS} on every call —
|
|
102
|
+
* useful for foundation tests that pre-date Settings wiring.
|
|
103
|
+
*/
|
|
104
|
+
private readonly settingsService?;
|
|
105
|
+
/**
|
|
106
|
+
* Feature 061 — the catalog's composed attribute read model (Principle I:
|
|
107
|
+
* replaces the former direct `ProductAttribute` entity find).
|
|
108
|
+
*/
|
|
109
|
+
private readonly catalogAttributes?;
|
|
110
|
+
/**
|
|
111
|
+
* Issue #132 — the pricing engine, through the `pricingService` port. A
|
|
112
|
+
* comparison exists so a buyer can put prices side by side, so the figures
|
|
113
|
+
* in its columns have to be the ones a price list stands behind.
|
|
114
|
+
*/
|
|
115
|
+
private readonly listingPrices?;
|
|
116
|
+
/**
|
|
117
|
+
* `organizations`' read model, for the one field the pricing engine needs
|
|
118
|
+
* beyond the viewer's organisation id: the customer group it belongs to,
|
|
119
|
+
* which a group-targeted price list is selected by. Without it a buyer's
|
|
120
|
+
* comparison column and their own cart line would resolve against
|
|
121
|
+
* different lists and quote different figures for one product.
|
|
122
|
+
*/
|
|
123
|
+
private readonly organizationDetails?;
|
|
124
|
+
/**
|
|
125
|
+
* Feature 087 Group B / D-187 — `customer_accounts`' read model, for the
|
|
126
|
+
* one field the owner stamp needs: the organisation the owning account
|
|
127
|
+
* belongs to.
|
|
128
|
+
*
|
|
129
|
+
* The organisation is derived from the **row**, never from the ambient
|
|
130
|
+
* request context, and that is what makes one implementation serve both
|
|
131
|
+
* write paths: `addProduct` runs on a storefront request and
|
|
132
|
+
* `adoptAnonymousComparison` runs inside another module's login hook, where
|
|
133
|
+
* there is no comparison caller to read a context from.
|
|
134
|
+
*
|
|
135
|
+
* Optional in the constructor and required at the two call sites, in the
|
|
136
|
+
* shape {@link organizationDetails} already takes here. A rig that wired no
|
|
137
|
+
* port cannot stamp, and the honest answer to that is a refusal at the
|
|
138
|
+
* write rather than a row `comparisons_organization_attribution_chk` will
|
|
139
|
+
* reject with a message about a constraint the caller never heard of.
|
|
140
|
+
*/
|
|
141
|
+
private readonly customerAccounts?;
|
|
142
|
+
constructor(emFactory: () => EntityManager, projection: ComparableAttributeProjection, tokens: ShareTokenGenerator,
|
|
143
|
+
/**
|
|
144
|
+
* `catalog`'s product read model. Not optional: a comparison is a list of
|
|
145
|
+
* products, so a service that cannot read one has nothing to answer with —
|
|
146
|
+
* where the settings and pricing arguments below have a defined fallback,
|
|
147
|
+
* this has none.
|
|
148
|
+
*/
|
|
149
|
+
catalogProducts: CatalogProductReadPort,
|
|
150
|
+
/**
|
|
151
|
+
* The sanctioned bridge accessor (Constitution XII), for the assortment
|
|
152
|
+
* gate `addProduct` owes (issue #259). Required for the same reason
|
|
153
|
+
* `catalogProducts` is: an optional one would spell "this rig wired no
|
|
154
|
+
* membership service" and "this channel publishes the product" as the same
|
|
155
|
+
* answer.
|
|
156
|
+
*/
|
|
157
|
+
channelMembership: SalesChannelMembershipPort,
|
|
158
|
+
/**
|
|
159
|
+
* Feature 075 / D-87 — `catalog`'s gallery read model, for the base image
|
|
160
|
+
* of each column. Required for the same reason `catalogProducts` is: the
|
|
161
|
+
* alternative to a wired port is not "a comparison without pictures", it is
|
|
162
|
+
* a service that cannot tell an operator who set no base image from a rig
|
|
163
|
+
* that wired no owner.
|
|
164
|
+
*
|
|
165
|
+
* `baseImageUrls`, not `list`: the read is a batch, it must tolerate an id
|
|
166
|
+
* whose product has since been removed, and the value a column renders is a
|
|
167
|
+
* **url**, which `catalog` resolves through the asset port it holds. Those
|
|
168
|
+
* three are exactly why the single join this replaced could not be swapped
|
|
169
|
+
* for the per-product port that already existed.
|
|
170
|
+
*/
|
|
171
|
+
catalogGallery: CatalogGalleryPort,
|
|
172
|
+
/**
|
|
173
|
+
* Optional settings service. When undefined, `compare.max_products`
|
|
174
|
+
* defaults to {@link DEFAULT_COMPARE_MAX_PRODUCTS} on every call —
|
|
175
|
+
* useful for foundation tests that pre-date Settings wiring.
|
|
176
|
+
*/
|
|
177
|
+
settingsService?: SettingsReadPort | undefined,
|
|
178
|
+
/**
|
|
179
|
+
* Feature 061 — the catalog's composed attribute read model (Principle I:
|
|
180
|
+
* replaces the former direct `ProductAttribute` entity find).
|
|
181
|
+
*/
|
|
182
|
+
catalogAttributes?: CatalogAttributeReadPort | undefined,
|
|
183
|
+
/**
|
|
184
|
+
* Issue #132 — the pricing engine, through the `pricingService` port. A
|
|
185
|
+
* comparison exists so a buyer can put prices side by side, so the figures
|
|
186
|
+
* in its columns have to be the ones a price list stands behind.
|
|
187
|
+
*/
|
|
188
|
+
listingPrices?: ListingPricePort | undefined,
|
|
189
|
+
/**
|
|
190
|
+
* `organizations`' read model, for the one field the pricing engine needs
|
|
191
|
+
* beyond the viewer's organisation id: the customer group it belongs to,
|
|
192
|
+
* which a group-targeted price list is selected by. Without it a buyer's
|
|
193
|
+
* comparison column and their own cart line would resolve against
|
|
194
|
+
* different lists and quote different figures for one product.
|
|
195
|
+
*/
|
|
196
|
+
organizationDetails?: OrganizationDetailsPort | undefined,
|
|
197
|
+
/**
|
|
198
|
+
* Feature 087 Group B / D-187 — `customer_accounts`' read model, for the
|
|
199
|
+
* one field the owner stamp needs: the organisation the owning account
|
|
200
|
+
* belongs to.
|
|
201
|
+
*
|
|
202
|
+
* The organisation is derived from the **row**, never from the ambient
|
|
203
|
+
* request context, and that is what makes one implementation serve both
|
|
204
|
+
* write paths: `addProduct` runs on a storefront request and
|
|
205
|
+
* `adoptAnonymousComparison` runs inside another module's login hook, where
|
|
206
|
+
* there is no comparison caller to read a context from.
|
|
207
|
+
*
|
|
208
|
+
* Optional in the constructor and required at the two call sites, in the
|
|
209
|
+
* shape {@link organizationDetails} already takes here. A rig that wired no
|
|
210
|
+
* port cannot stamp, and the honest answer to that is a refusal at the
|
|
211
|
+
* write rather than a row `comparisons_organization_attribution_chk` will
|
|
212
|
+
* reject with a message about a constraint the caller never heard of.
|
|
213
|
+
*/
|
|
214
|
+
customerAccounts?: CustomerAccountReadPort | undefined);
|
|
215
|
+
getForOwner(owner: ComparisonOwner): Promise<Comparison | null>;
|
|
216
|
+
/**
|
|
217
|
+
* Resolve a share token to its comparison, across customers.
|
|
218
|
+
*
|
|
219
|
+
* `Comparison` is `@CustomerScoped`, so the ambient filter confines this read
|
|
220
|
+
* to the caller's own rows — which is right for every other method here and
|
|
221
|
+
* is the one thing a share link has to cross. An **anonymous** recipient
|
|
222
|
+
* already crossed it by accident: the composition roots run anonymous
|
|
223
|
+
* requests under `systemTenantContext('actor:anonymous')`, so the filter is a
|
|
224
|
+
* no-op for them, while a *signed-in* recipient got a 404 on a link that
|
|
225
|
+
* worked when they were logged out. That is the shape a guard takes when a
|
|
226
|
+
* deliberate cross-tenant grant is left implicit.
|
|
227
|
+
*
|
|
228
|
+
* So it is explicit, through the one sanctioned crossing (feature 050,
|
|
229
|
+
* FR-005): the token is the authorization, exactly as it is on the public
|
|
230
|
+
* product-feed endpoint, and the widening covers this single lookup by a
|
|
231
|
+
* unique index and nothing after it. **What the recipient then sees is not
|
|
232
|
+
* widened at all** — `buildOwnerView` filters the products by their own
|
|
233
|
+
* audience and prices them for their own organisation.
|
|
234
|
+
*/
|
|
235
|
+
findByShareToken(token: string): Promise<Comparison | null>;
|
|
236
|
+
/**
|
|
237
|
+
* Anonymous → authenticated merge (research.md R-2 / spec FR-005).
|
|
238
|
+
* Called from the post-login hook with the cookie-bound
|
|
239
|
+
* `anonymousToken` the caller was carrying. Behaviour:
|
|
240
|
+
*
|
|
241
|
+
* - customer has no Comparison → reassign the anonymous one
|
|
242
|
+
* (set customer_account_id, clear anonymous_token).
|
|
243
|
+
* - customer already has a Comparison → discard the anonymous one
|
|
244
|
+
* (the customer's existing curated set wins, per R-2 — merging
|
|
245
|
+
* two sets is more surprising than honouring the persistent
|
|
246
|
+
* identity).
|
|
247
|
+
*
|
|
248
|
+
* No-op when neither side resolves.
|
|
249
|
+
*
|
|
250
|
+
* **This is the one place in the platform where a comparison becomes owned**
|
|
251
|
+
* — `Cart` has no counterpart, because its anonymous merge *completes* the
|
|
252
|
+
* anonymous cart rather than re-owning it. So it is the write feature 087's
|
|
253
|
+
* invariant would have been forgotten at: the row arrives here with a null
|
|
254
|
+
* `organization_id` that was correct while it was anonymous, and the
|
|
255
|
+
* organisation is set in the same unit of work as the account, from the same
|
|
256
|
+
* account. Nothing here is conditional on the column being null — an
|
|
257
|
+
* anonymous row's organisation is null by construction, and if it ever is
|
|
258
|
+
* not, the account it is being handed to is the truth.
|
|
259
|
+
*/
|
|
260
|
+
adoptAnonymousComparison(customerAccountId: string, anonymousToken: string): Promise<void>;
|
|
261
|
+
/**
|
|
262
|
+
* True when the supplied owner identity matches the Comparison's
|
|
263
|
+
* stored owner. Used by the share-token endpoint to populate
|
|
264
|
+
* `meta.viewerIsOwner`.
|
|
265
|
+
*/
|
|
266
|
+
isOwnedBy(comparison: Comparison, owner: ComparisonOwner | null): boolean;
|
|
267
|
+
addProduct(owner: ComparisonOwner, salesChannelId: string, productId: string,
|
|
268
|
+
/**
|
|
269
|
+
* Who is comparing (issue #227). Defaulted to the anonymous audience so a
|
|
270
|
+
* composition that has not been taught to resolve its caller refuses a
|
|
271
|
+
* restricted product rather than adding it — the fail-closed end. The
|
|
272
|
+
* public route passes the real one.
|
|
273
|
+
*/
|
|
274
|
+
audience?: ProductAudience): Promise<Comparison>;
|
|
275
|
+
removeProduct(owner: ComparisonOwner, productId: string): Promise<Comparison>;
|
|
276
|
+
setDisplayMode(owner: ComparisonOwner, mode: ComparisonDisplayMode): Promise<Comparison>;
|
|
277
|
+
deleteForOwner(owner: ComparisonOwner): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* Build the wire-format `ComparisonOwnerView` for the given comparison
|
|
280
|
+
* in the viewer's sales-channel context. Reusable across the three
|
|
281
|
+
* consumers (owner / recipient / admin); the recipient + admin views
|
|
282
|
+
* pass `viewerIsOwner=false` and consume only the slice they need.
|
|
283
|
+
*
|
|
284
|
+
* **The viewer's identity decides what the viewer sees. Always.** A share
|
|
285
|
+
* token grants access to the *comparison*, never to anything in it, so it
|
|
286
|
+
* settles two questions the same way:
|
|
287
|
+
*
|
|
288
|
+
* - **the prices** — the owner sees their organisation's figures, a
|
|
289
|
+
* signed-in recipient from another organisation sees **theirs**, and an
|
|
290
|
+
* anonymous recipient sees the channel's. That is what makes a shared
|
|
291
|
+
* link trustworthy: the numbers are true for whoever is looking, and a
|
|
292
|
+
* recipient who acted on the sender's negotiated price would be acting on
|
|
293
|
+
* a price nobody will sell them at.
|
|
294
|
+
* - **the products** — a row restricted to the sender's organisation is not
|
|
295
|
+
* disclosed to a recipient outside it, while a recipient who *is* on the
|
|
296
|
+
* allow-list sees it. The owner was entitled to add it (`addProduct`
|
|
297
|
+
* enforces that, issue #227); a second reader has an entitlement of their
|
|
298
|
+
* own, and a product may be restricted after it was added.
|
|
299
|
+
*
|
|
300
|
+
* The two run in that order — filter, then price — because a price resolved
|
|
301
|
+
* for a row the reader may not see is work done to produce a figure that must
|
|
302
|
+
* not be sent.
|
|
303
|
+
*/
|
|
304
|
+
buildOwnerView(comparison: Comparison, viewerSalesChannelId: string,
|
|
305
|
+
/**
|
|
306
|
+
* Who is looking. Defaulted to the anonymous buyer so a caller that has not
|
|
307
|
+
* been taught to resolve its reader shows the public set at the channel
|
|
308
|
+
* price — the answer every reader may see — rather than one buyer's.
|
|
309
|
+
*/
|
|
310
|
+
viewer?: ComparisonViewer): Promise<ComparisonOwnerView>;
|
|
311
|
+
/**
|
|
312
|
+
* Read `compare.max_products` from Settings; fall back to the manifest
|
|
313
|
+
* default on any error (Settings hiccup must not break the storefront).
|
|
314
|
+
*/
|
|
315
|
+
private resolveMaxProducts;
|
|
316
|
+
}
|
|
317
|
+
export type ComparisonOwner = {
|
|
318
|
+
kind: 'customer';
|
|
319
|
+
customerAccountId: string;
|
|
320
|
+
} | {
|
|
321
|
+
kind: 'anonymous';
|
|
322
|
+
anonymousToken: string;
|
|
323
|
+
};
|
|
324
|
+
export declare class ComparisonFullError extends Error {
|
|
325
|
+
readonly max: number;
|
|
326
|
+
constructor(max: number);
|
|
327
|
+
}
|
|
328
|
+
export declare class ComparisonNotFoundError extends Error {
|
|
329
|
+
constructor();
|
|
330
|
+
}
|
|
331
|
+
export declare class ProductNotInComparisonError extends Error {
|
|
332
|
+
readonly productId: string;
|
|
333
|
+
constructor(productId: string);
|
|
334
|
+
}
|
|
335
|
+
export declare class ProductNotFoundError extends Error {
|
|
336
|
+
readonly productId: string;
|
|
337
|
+
constructor(productId: string);
|
|
338
|
+
}
|
|
339
|
+
//# sourceMappingURL=comparison-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-service.d.ts","sourceRoot":"","sources":["../../../src/backend/services/comparison-service.ts"],"names":[],"mappings":"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"}
|