@faststore/api 4.10.1-dev.6 → 4.10.1-dev.7

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.
Files changed (33) hide show
  1. package/dist/cjs/index.js +49 -46
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/es/index.mjs +1115 -1058
  4. package/dist/es/index.mjs.map +1 -1
  5. package/dist/src/__generated__/schema.d.ts +1 -0
  6. package/dist/src/__generated__/schema.d.ts.map +1 -1
  7. package/dist/src/platforms/vtex/clients/index.d.ts +1 -1
  8. package/dist/src/platforms/vtex/clients/search/index.d.ts +7 -1
  9. package/dist/src/platforms/vtex/clients/search/index.d.ts.map +1 -1
  10. package/dist/src/platforms/vtex/index.d.ts +1 -1
  11. package/dist/src/platforms/vtex/index.d.ts.map +1 -1
  12. package/dist/src/platforms/vtex/loaders/sku.d.ts.map +1 -1
  13. package/dist/src/platforms/vtex/resolvers/index.d.ts +1 -1
  14. package/dist/src/platforms/vtex/resolvers/index.d.ts.map +1 -1
  15. package/dist/src/platforms/vtex/resolvers/query.d.ts +1 -1
  16. package/dist/src/platforms/vtex/resolvers/query.d.ts.map +1 -1
  17. package/dist/src/platforms/vtex/resolvers/validateSession.d.ts.map +1 -1
  18. package/dist/src/platforms/vtex/utils/contractAssortment.d.ts +22 -0
  19. package/dist/src/platforms/vtex/utils/contractAssortment.d.ts.map +1 -0
  20. package/dist/src/platforms/vtex/utils/intelligentSearchRequest.d.ts +5 -0
  21. package/dist/src/platforms/vtex/utils/intelligentSearchRequest.d.ts.map +1 -1
  22. package/dist/src/platforms/vtex/utils/validateSessionHelpers.d.ts +29 -2
  23. package/dist/src/platforms/vtex/utils/validateSessionHelpers.d.ts.map +1 -1
  24. package/package.json +3 -3
  25. package/src/__generated__/schema.ts +1 -0
  26. package/src/platforms/vtex/clients/search/index.ts +13 -1
  27. package/src/platforms/vtex/loaders/sku.ts +1 -0
  28. package/src/platforms/vtex/resolvers/query.ts +51 -37
  29. package/src/platforms/vtex/resolvers/validateSession.ts +3 -3
  30. package/src/platforms/vtex/typeDefs/session.graphql +3 -0
  31. package/src/platforms/vtex/utils/contractAssortment.ts +87 -0
  32. package/src/platforms/vtex/utils/intelligentSearchRequest.ts +10 -0
  33. package/src/platforms/vtex/utils/validateSessionHelpers.ts +47 -5
@@ -199,7 +199,7 @@ export declare const getResolvers: () => {
199
199
  accountProfile: (_: unknown, __: unknown, ctx: import('..').GraphqlContext) => Promise<{
200
200
  name: string;
201
201
  email: string;
202
- id: any;
202
+ id: string;
203
203
  } | null>;
204
204
  availableContracts: (_: unknown, { orgUnitId }: import('../../..').QueryAvailableContractsArgs, ctx: import('..').GraphqlContext) => Promise<import('../../..').StoreContract[]>;
205
205
  pickupPoints: (_: unknown, { geoCoordinates, postalCode, country, channel }: import('../../..').QueryPickupPointsArgs, ctx: import('..').GraphqlContext) => Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/resolvers/index.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY;;;;;;oJAC6uG,mCAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAAs0hB,CAAC;mBAAsB,CAAC;kBAAqB,CAAC;yBAA8B,CAAC;uBAA0B,CAAC;yBAA4B,CAAC;uBAA0B,CAAC;iBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAAn8mB,CAAC;yBAAoB,CAAC;0BAAqB,CAAC;0BAAqB,CAAC;8BAAyB,CAAC;gCAA2B,CAAC;;;6BAA0D,CAAC;+BAA0B,CAAC;+BAA0B,CAAC;;;;;;;;;;;wBAA4jnC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CADpkpC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/resolvers/index.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY;;;;;;oJAC6uG,mCAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAAqyiB,CAAC;mBAAsB,CAAC;kBAAqB,CAAC;yBAA8B,CAAC;uBAA0B,CAAC;yBAA4B,CAAC;uBAA0B,CAAC;iBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAAznnB,CAAC;yBAAoB,CAAC;0BAAqB,CAAC;0BAAqB,CAAC;8BAAyB,CAAC;gCAA2B,CAAC;;;6BAA0D,CAAC;+BAA0B,CAAC;+BAA0B,CAAC;;;;;;;;;;;wBAAmxmC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CADpkpC,CAAA"}
@@ -159,7 +159,7 @@ export declare const Query: {
159
159
  accountProfile: (_: unknown, __: unknown, ctx: GraphqlContext) => Promise<{
160
160
  name: string;
161
161
  email: string;
162
- id: any;
162
+ id: string;
163
163
  } | null>;
164
164
  availableContracts: (_: unknown, { orgUnitId }: QueryAvailableContractsArgs, ctx: GraphqlContext) => Promise<StoreContract[]>;
165
165
  pickupPoints: (_: unknown, { geoCoordinates, postalCode, country, channel }: QueryPickupPointsArgs, ctx: GraphqlContext) => Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/resolvers/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,uBAAuB,EACvB,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAElB,aAAa,EAEd,MAAM,+BAA+B,CAAA;AAUtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAA;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,uBAAuB,CAAA;AAW9B,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAoIlE,eAAO,MAAM,KAAK;iBAEX,OAAO,eACG,gBAAgB,OACxB,cAAc;oBAyChB,OAAO,YACA,mBAAmB,OACxB,cAAc;gBAgBhB,OAAO,6EAQP,eAAe,OACb,cAAc;;;;qBA4EhB,OAAO,gCACoB,oBAAoB,OAC7C,cAAc;;;;;;;;;;;;;kBAkChB,OAAO,kBACM,iBAAiB,OAC5B,cAAc;wBAwBhB,OAAO,gCACoB,uBAAuB,OAChD,cAAc;;;;;;;;;;;;;kBA8EhB,OAAO,kCACsB,iBAAiB,OAC5C,cAAc;;;;;;;;;;;;;;;;;kBAkBhB,OAAO,4BACgB,iBAAiB,OACtC,cAAc;;;iBAqBhB,OAAO,yDAC6C,gBAAgB,OAClE,cAAc;;;;iBAqBhB,OAAO,UACF,gBAAgB,OACnB,cAAc;;;sBAyBhB,OAAO,YACA,qBAAqB,OAC1B,cAAc;mBAahB,OAAO,eACG,kBAAkB,OAC1B,cAAc;;;;;;;;;;;;;;;;;;;;;;;wBAqGhB,OAAO,WACD,uBAAuB,OAC3B,cAAc;;;;;;;;;;;;;;;yBAwBM,OAAO,MAAM,OAAO,OAAO,cAAc;;;;;;;;;;;;;4BAqBtC,OAAO,MAAM,OAAO,OAAO,cAAc;wBA0BlE,OAAO,WACD;QACP,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;QACjB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,OACI,cAAc;;;;;;;;;;;;;;;;sBAoDG,OAAO,MAAM,OAAO,QAAQ,cAAc;;;8BAQ7D,OAAO,MACN,OAAO,OACN,cAAc;qBAWE,OAAO,MAAM,OAAO,OAAO,cAAc;;;;;;;;wBAqCtC,OAAO,MAAM,OAAO,OAAO,cAAc;;;;;4BA2E9D,OAAO,iBACK,2BAA2B,OACrC,cAAc,KAClB,OAAO,CAAC,aAAa,EAAE,CAAC;sBA4DtB,OAAO,oDACwC,qBAAqB,OAClE,cAAc;;;;;;;oBAv7BI,CAAA;qBAAoB,CAAC;sBACnC,CAAC;sBAAsB,CAAA;0BAGjC,CAAC;4BAA2B,CAAC;;;yBACtB,CAAC;2BAA0B,CAAC;2BAA0B,CAAC;;;;;;;;;;;oBAg9BwsR,CAAC;;;;;;;;;;;;;;;;;;;;CADzwR,CAAA"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/resolvers/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAC7B,uBAAuB,EACvB,oBAAoB,EACpB,2BAA2B,EAC3B,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAElB,aAAa,EAEd,MAAM,+BAA+B,CAAA;AAUtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAA;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,uBAAuB,CAAA;AAY9B,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAwIlE,eAAO,MAAM,KAAK;iBAEX,OAAO,eACG,gBAAgB,OACxB,cAAc;oBAmDhB,OAAO,YACA,mBAAmB,OACxB,cAAc;gBAgBhB,OAAO,6EAQP,eAAe,OACb,cAAc;;;;qBA4EhB,OAAO,gCACoB,oBAAoB,OAC7C,cAAc;;;;;;;;;;;;;kBAkChB,OAAO,kBACM,iBAAiB,OAC5B,cAAc;wBAwBhB,OAAO,gCACoB,uBAAuB,OAChD,cAAc;;;;;;;;;;;;;kBA8EhB,OAAO,kCACsB,iBAAiB,OAC5C,cAAc;;;;;;;;;;;;;;;;;kBAkBhB,OAAO,4BACgB,iBAAiB,OACtC,cAAc;;;iBAqBhB,OAAO,yDAC6C,gBAAgB,OAClE,cAAc;;;;iBAqBhB,OAAO,UACF,gBAAgB,OACnB,cAAc;;;sBAyBhB,OAAO,YACA,qBAAqB,OAC1B,cAAc;mBAahB,OAAO,eACG,kBAAkB,OAC1B,cAAc;;;;;;;;;;;;;;;;;;;;;;;wBAqGhB,OAAO,WACD,uBAAuB,OAC3B,cAAc;;;;;;;;;;;;;;;yBAwBM,OAAO,MAAM,OAAO,OAAO,cAAc;;;;;;;;;;;;;4BAqBtC,OAAO,MAAM,OAAO,OAAO,cAAc;wBA0BlE,OAAO,WACD;QACP,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;QACjB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,OACI,cAAc;;;;;;;;;;;;;;;;sBAoDG,OAAO,MAAM,OAAO,QAAQ,cAAc;;;8BAQ7D,OAAO,MACN,OAAO,OACN,cAAc;qBAWE,OAAO,MAAM,OAAO,OAAO,cAAc;;;;;;;;wBAqCtC,OAAO,MAAM,OAAO,OAAO,cAAc;;;;;4BA0E9D,OAAO,iBACK,2BAA2B,OACrC,cAAc,KAClB,OAAO,CAAC,aAAa,EAAE,CAAC;sBA4DtB,OAAO,oDACwC,qBAAqB,OAClE,cAAc;;;;;;;oBA57BmB,CAAC;qBAAoB,CAAC;sBAC3D,CAAC;sBAAqB,CAAC;0BAAyB,CAAC;4BAClD,CAAC;;;yBAA0D,CAAC;2BACrD,CAAC;2BAA2B,CAAA;;;;;;;;;;;oBAu9BguQ,CAAC;;;;;;;;;;;;;;;;;;;;CADvwQ,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"validateSession.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/resolvers/validateSession.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AACxC,OAAO,KAAK,EACV,2BAA2B,EAC3B,YAAY,EACb,MAAM,+BAA+B,CAAA;AAsItC,eAAO,MAAM,eAAe,GAC1B,GAAG,GAAG,EACN,iCAAiC,2BAA2B,EAC5D,yDAAyD,cAAc,KACtE,OAAO,CAAC,YAAY,GAAG,IAAI,CAgJ7B,CAAA"}
1
+ {"version":3,"file":"validateSession.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/resolvers/validateSession.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AACxC,OAAO,KAAK,EACV,2BAA2B,EAC3B,YAAY,EACb,MAAM,+BAA+B,CAAA;AAuItC,eAAO,MAAM,eAAe,GAC1B,GAAG,GAAG,EACN,iCAAiC,2BAA2B,EAC5D,yDAAyD,cAAc,KACtE,OAAO,CAAC,YAAY,GAAG,IAAI,CA+I7B,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { ProductCluster } from '../clients/search/types/ProductSearchResult';
2
+ import { GraphqlContext } from '../index';
3
+ import { EnhancedSku } from './enhanceSku';
4
+ export interface ProductClusterRestriction {
5
+ allowed: Set<string>;
6
+ excluded: Set<string>;
7
+ }
8
+ /**
9
+ * Collections the shopper's assortment is restricted to, as carried by the
10
+ * `vtex_segment` cookie (`productClusterIds=X` allows, `productClusterIds=not:Y`
11
+ * excludes). Returns `null` when the segment carries no restriction.
12
+ */
13
+ export declare function getProductClusterRestriction(segment: Record<string, unknown>): ProductClusterRestriction | null;
14
+ export declare function isProductInAssortment(productClusters: ProductCluster[] | undefined, { allowed, excluded }: ProductClusterRestriction): boolean;
15
+ /**
16
+ * Listing endpoints get the assortment applied by Intelligent Search through
17
+ * the segment facets, but the `products` endpoint used to fetch a single
18
+ * product does not, so a product outside the shopper's assortment must be
19
+ * rejected here.
20
+ */
21
+ export declare function assertProductInContractAssortment(ctx: GraphqlContext, sku: EnhancedSku): void;
22
+ //# sourceMappingURL=contractAssortment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contractAssortment.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/utils/contractAssortment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAS/C,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACpB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CACtB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,yBAAyB,GAAG,IAAI,CAwBlC;AAED,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,cAAc,EAAE,GAAG,SAAS,EAC7C,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,yBAAyB,GAC/C,OAAO,CAQT;AAED;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,WAAW,GACf,IAAI,CAcN"}
@@ -50,6 +50,11 @@ export interface IntelligentSearchRequest {
50
50
  * Returns an empty object when the cookie is absent or invalid.
51
51
  */
52
52
  export declare function parseSegmentCookie(cookieHeader?: string): Record<string, unknown>;
53
+ /**
54
+ * Non-shipping facets carried by the `vtex_segment` cookie (e.g. the B2B
55
+ * contract's `productClusterIds`), in the order they appear in the cookie.
56
+ */
57
+ export declare function getSegmentExtraFacets(segment: Record<string, unknown>): IntelligentSearchFacet[];
53
58
  /**
54
59
  * Pure builder for intelligent-search v1 path + query params.
55
60
  * Callers assemble the full URL: `/api/intelligent-search/v1/${endpoint}/${request.path}?${request.params}`.
@@ -1 +1 @@
1
- {"version":3,"file":"intelligentSearchRequest.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/utils/intelligentSearchRequest.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AA6BD,MAAM,MAAM,IAAI,GACZ,YAAY,GACZ,WAAW,GACX,aAAa,GACb,WAAW,GACX,UAAU,GACV,cAAc,GACd,eAAe,GACf,EAAE,GAIF,GAAG,MAAM,IAAI,KAAK,GAAG,MAAM,EAAE,CAAA;AAEjC,MAAM,MAAM,sBAAsB,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,KAAK,CAAA;AAEhF,MAAM,MAAM,yBAAyB,GACjC,gBAAgB,GAChB,QAAQ,GACR,eAAe,GACf,UAAU,GACV,oBAAoB,GACpB,cAAc,GACd,2BAA2B,CAAA;AAE/B,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,kBAAkB,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;IAClD,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,cAAc,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACzC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,KAAK,CAAC,EAAE,sBAAsB,CAAA;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,yBAAyB,CAAA;IACnC,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,CAAC,EAAE,yBAAyB,CAAA;IACpC,IAAI,CAAC,EAAE,4BAA4B,CAAA;CACpC;AAED,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,IAAI,MAAM,CAAA;CACnB;AAwCD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;AAgfD;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,6BAA6B,GACnC,wBAAwB,CAkE1B"}
1
+ {"version":3,"file":"intelligentSearchRequest.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/utils/intelligentSearchRequest.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AA6BD,MAAM,MAAM,IAAI,GACZ,YAAY,GACZ,WAAW,GACX,aAAa,GACb,WAAW,GACX,UAAU,GACV,cAAc,GACd,eAAe,GACf,EAAE,GAIF,GAAG,MAAM,IAAI,KAAK,GAAG,MAAM,EAAE,CAAA;AAEjC,MAAM,MAAM,sBAAsB,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,KAAK,CAAA;AAEhF,MAAM,MAAM,yBAAyB,GACjC,gBAAgB,GAChB,QAAQ,GACR,eAAe,GACf,UAAU,GACV,oBAAoB,GACpB,cAAc,GACd,2BAA2B,CAAA;AAE/B,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,kBAAkB,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;IAClD,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,cAAc,CAAC,EAAE,sBAAsB,EAAE,CAAA;IACzC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,KAAK,CAAC,EAAE,sBAAsB,CAAA;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,yBAAyB,CAAA;IACnC,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,CAAC,EAAE,yBAAyB,CAAA;IACpC,IAAI,CAAC,EAAE,4BAA4B,CAAA;CACpC;AAED,MAAM,WAAW,wBAAwB;IACvC,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,IAAI,MAAM,CAAA;CACnB;AAwCD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB;AA6DD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,sBAAsB,EAAE,CAE1B;AAqbD;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,6BAA6B,GACnC,wBAAwB,CAkE1B"}
@@ -12,13 +12,37 @@ export declare function getPreciseLocationData(clients: GraphqlContext['clients'
12
12
  export declare function buildSessionSearchParams(search: string, channel: Required<Channel>, postalCode: string, country: string, geoCoordinates: StoreSession['geoCoordinates'], locale: string): URLSearchParams;
13
13
  export declare function buildMarketingData(params: URLSearchParams, oldMarketingData: StoreSession['marketingData']): StoreMarketingData;
14
14
  export declare function resolveJwtClaims(clients: GraphqlContext['clients'], cookie: string | undefined, account: string): Promise<{
15
- isRepresentative: boolean;
16
15
  customerId: any;
17
16
  unitId: any;
18
17
  }>;
19
18
  export declare function resolveSellerInRegion(clients: GraphqlContext['clients'], channel: Required<Channel>, postalCode: string, geoCoordinates: StoreSession['geoCoordinates'], country: string, salesChannel: string): Promise<string | undefined>;
19
+ /**
20
+ * First value that is a non-blank string, trimmed. Session values and JWT
21
+ * claims are only nominally typed, so anything else is skipped.
22
+ */
23
+ export declare function firstNonBlank(...values: unknown[]): string;
24
+ /**
25
+ * Organizational unit of the shopper, from the session or the JWT claim.
26
+ * Buyer Portal identifies its buyers by the unit; `isRepresentative` alone is
27
+ * not enough, since it can be true outside B2B.
28
+ */
29
+ export declare function resolveB2bUnitId(authentication: {
30
+ unitId?: {
31
+ value?: string | null;
32
+ } | null;
33
+ } | null, jwtUnitId?: unknown): string;
34
+ /**
35
+ * Returns `null` unless the shopper has both an organizational unit and a
36
+ * contract, the rule Buyer Portal uses for a B2B session.
37
+ *
38
+ * The contract falls back from the session's active contract to the session's
39
+ * authentication namespace and then to the JWT claim. Blank values fall
40
+ * through, so an empty session value never hides the claim.
41
+ *
42
+ * The deprecated `isRepresentative` field stays `true` whenever `b2b` is
43
+ * returned, so consumers that still read it keep working.
44
+ */
20
45
  export declare function buildB2bSession(args: {
21
- isRepresentative: boolean;
22
46
  authentication: {
23
47
  customerId?: {
24
48
  value?: string | null;
@@ -43,6 +67,9 @@ export declare function buildB2bSession(args: {
43
67
  organizationManager?: {
44
68
  value?: boolean | null;
45
69
  } | null;
70
+ activeContractId?: {
71
+ value?: string | null;
72
+ } | null;
46
73
  } | null;
47
74
  publicData: {
48
75
  postalCode?: {
@@ -1 +1 @@
1
- {"version":3,"file":"validateSessionHelpers.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/utils/validateSessionHelpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validateSessionHelpers.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/vtex/utils/validateSessionHelpers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/api",
3
- "version": "4.10.1-dev.6",
3
+ "version": "4.10.1-dev.7",
4
4
  "license": "MIT",
5
5
  "main": "dist/cjs/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -58,11 +58,11 @@
58
58
  "vite": "7.1.5",
59
59
  "vite-plugin-dts": "4.5.4",
60
60
  "vitest": "4.0.18",
61
- "@faststore/diagnostics": "^4.10.1-dev.6"
61
+ "@faststore/diagnostics": "^4.10.1-dev.7"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "graphql": "16.11.0",
65
- "@faststore/diagnostics": "4.10.1-dev.6"
65
+ "@faststore/diagnostics": "4.10.1-dev.7"
66
66
  },
67
67
  "publishConfig": {
68
68
  "access": "public"
@@ -1360,6 +1360,7 @@ export type StoreB2B = {
1360
1360
  contractName?: Maybe<Scalars['String']['output']>;
1361
1361
  customerId: Scalars['String']['output'];
1362
1362
  firstName?: Maybe<Scalars['String']['output']>;
1363
+ /** @deprecated Always true when `b2b` is set: a B2B session is identified by `unitId` and `customerId`. Check that `b2b` is not null instead. Scheduled for removal in the next major. */
1363
1364
  isRepresentative?: Maybe<Scalars['Boolean']['output']>;
1364
1365
  lastName?: Maybe<Scalars['String']['output']>;
1365
1366
  organizationManager?: Maybe<Scalars['Boolean']['output']>;
@@ -2,6 +2,7 @@ import pLimit from 'p-limit'
2
2
  import type { Options } from '../../../../typings/globals'
3
3
 
4
4
  import type { GraphqlContext } from '../../'
5
+ import { isNotFoundError } from '../../../errors'
5
6
  import { getWithCookie } from '../../utils/cookies'
6
7
  import type { SelectedFacet } from '../../utils/facets'
7
8
  import { isLocalizationEnabled } from '../../utils/localization'
@@ -65,6 +66,12 @@ export interface ProductsByIdentifierArgs {
65
66
  values: string[]
66
67
  showInvisibleItems?: boolean
67
68
  hideUnavailableItems?: boolean
69
+ /**
70
+ * Rethrow upstream failures (5xx, timeouts, network) instead of dropping the
71
+ * product, so callers can tell a failure from a product that does not exist.
72
+ * A not-found answer is still dropped.
73
+ */
74
+ throwOnUpstreamError?: boolean
68
75
  }
69
76
 
70
77
  export interface PickupPointAvailabilityArgs {
@@ -214,6 +221,7 @@ export const IntelligentSearch = (
214
221
  values,
215
222
  showInvisibleItems,
216
223
  hideUnavailableItems: hideUnavailable,
224
+ throwOnUpstreamError = false,
217
225
  }: ProductsByIdentifierArgs): Promise<Product[]> => {
218
226
  const limit = pLimit(PRODUCTS_BY_IDENTIFIER_CONCURRENCY)
219
227
 
@@ -227,7 +235,11 @@ export const IntelligentSearch = (
227
235
  hideUnavailableItems: hideUnavailable,
228
236
  showInvisibleItems,
229
237
  })
230
- } catch {
238
+ } catch (error) {
239
+ if (throwOnUpstreamError && !isNotFoundError(error)) {
240
+ throw error
241
+ }
242
+
231
243
  return null
232
244
  }
233
245
  })
@@ -17,6 +17,7 @@ export const getSkuLoader = ({ flags }: Options, clients: Clients) => {
17
17
  field: 'sku',
18
18
  values: skuIds,
19
19
  showInvisibleItems,
20
+ throwOnUpstreamError: true,
20
21
  ...(flags?.enableUnavailableItemsOnCart && {
21
22
  hideUnavailableItems: false,
22
23
  }),
@@ -44,6 +44,7 @@ import {
44
44
  resolveActiveContractIdFromSession,
45
45
  resolveDefaultContractId,
46
46
  } from '../utils/contract'
47
+ import { assertProductInContractAssortment } from '../utils/contractAssortment'
47
48
  import { mutateChannelContext, mutateLocaleContext } from '../utils/contex'
48
49
  import { getAuthCookie, parseJwt } from '../utils/cookies'
49
50
  import { enhanceSku, type EnhancedSku } from '../utils/enhanceSku'
@@ -59,6 +60,10 @@ import { getCatalogLocale, isLocalizationEnabled } from '../utils/localization'
59
60
  import { isValidSkuId, pickBestSku } from '../utils/sku'
60
61
  import { slugify } from '../utils/slugify'
61
62
  import { resolveSort } from '../utils/sort'
63
+ import {
64
+ firstNonBlank,
65
+ resolveB2bUnitId,
66
+ } from '../utils/validateSessionHelpers'
62
67
  import { FACET_CROSS_SELLING_MAP } from './../utils/facets'
63
68
  import { StoreCollection } from './collection'
64
69
  import { getOrderEntryOperation } from './getOrderEntryOperation'
@@ -202,25 +207,35 @@ export const Query = {
202
207
  loaders: { skuLoader },
203
208
  } = ctx
204
209
 
205
- try {
206
- const skuId = id ?? slug?.split('-').pop() ?? ''
210
+ const loadSku = async () => {
211
+ try {
212
+ const skuId = id ?? slug?.split('-').pop() ?? ''
207
213
 
208
- if (!isValidSkuId(skuId)) {
209
- throw new Error(INVALID_SKU_ID_ERROR)
210
- }
214
+ if (!isValidSkuId(skuId)) {
215
+ throw new Error(INVALID_SKU_ID_ERROR)
216
+ }
211
217
 
212
- const sku = await skuLoader.load(skuId)
218
+ const sku = await skuLoader.load(skuId)
213
219
 
214
- await assertSkuMatchesSlug(ctx, sku, slug, locale)
220
+ await assertSkuMatchesSlug(ctx, sku, slug, locale)
215
221
 
216
- return sku
217
- } catch (err) {
218
- if (!shouldFallbackToProductRoute(err)) {
219
- throw err
220
- }
222
+ return sku
223
+ } catch (err) {
224
+ if (!shouldFallbackToProductRoute(err)) {
225
+ throw err
226
+ }
221
227
 
222
- return fetchProductBySlugFallback(ctx, slug)
228
+ return fetchProductBySlugFallback(ctx, slug)
229
+ }
223
230
  }
231
+
232
+ const sku = await loadSku()
233
+
234
+ // Runs outside `loadSku` so its NotFoundError is not swallowed by the
235
+ // slug fallback, which would refetch and return the same product.
236
+ assertProductInContractAssortment(ctx, sku)
237
+
238
+ return sku
224
239
  },
225
240
  collection: (
226
241
  _: unknown,
@@ -871,8 +886,8 @@ export const Query = {
871
886
  orgUnit: authentication?.unitName?.value ?? '',
872
887
  }
873
888
  },
874
- // If isRepresentative, return b2b information.
875
- // If not, return b2c user information
889
+ // Buyers with an organizational unit and a contract get the contract
890
+ // information. Everyone else gets the b2c user information.
876
891
  accountProfile: async (_: unknown, __: unknown, ctx: GraphqlContext) => {
877
892
  const {
878
893
  account,
@@ -886,7 +901,7 @@ export const Query = {
886
901
  return null
887
902
  }
888
903
 
889
- if (jwt?.isRepresentative) {
904
+ if (resolveB2bUnitId(null, jwt.unitId)) {
890
905
  const sessionData = await commerce.session('').catch(() => null)
891
906
 
892
907
  if (!sessionData) {
@@ -897,13 +912,14 @@ export const Query = {
897
912
  const shopper = sessionData.namespaces.shopper ?? null
898
913
  const authentication = sessionData.namespaces.authentication ?? null
899
914
 
900
- const contractId =
901
- resolveActiveContractIdFromSession(sessionData) ||
902
- jwt?.customerId?.trim() ||
903
- ''
915
+ const contractId = firstNonBlank(
916
+ shopper?.activeContractId?.value,
917
+ authentication?.customerId?.value,
918
+ jwt.customerId
919
+ )
904
920
 
905
- let contract = null
906
921
  if (contractId) {
922
+ let contract = null
907
923
  try {
908
924
  contract = await commerce.masterData.getContractById({ contractId })
909
925
  } catch (err) {
@@ -912,23 +928,21 @@ export const Query = {
912
928
  err
913
929
  )
914
930
  }
915
- }
916
-
917
- const shopperName =
918
- `${(shopper?.firstName?.value ?? '').trim()} ${(shopper?.lastName?.value ?? '').trim()}`.trim()
919
- const name =
920
- resolveActiveContractDisplayName(contract, profile) || shopperName
921
931
 
922
- return {
923
- name: name || '',
924
- email:
925
- profile?.email?.value || authentication?.storeUserEmail?.value || '',
926
- id:
927
- profile?.id?.value ||
928
- authentication?.customerId?.value ||
929
- jwt?.customerId?.trim() ||
930
- '',
931
- // createdAt: '',
932
+ const shopperName =
933
+ `${(shopper?.firstName?.value ?? '').trim()} ${(shopper?.lastName?.value ?? '').trim()}`.trim()
934
+ const name =
935
+ resolveActiveContractDisplayName(contract, profile) || shopperName
936
+
937
+ return {
938
+ name: name || '',
939
+ email:
940
+ profile?.email?.value ||
941
+ authentication?.storeUserEmail?.value ||
942
+ '',
943
+ id: profile?.id?.value || contractId,
944
+ // createdAt: '',
945
+ }
932
946
  }
933
947
  }
934
948
 
@@ -22,6 +22,7 @@ import {
22
22
  buildPersonFromProfile,
23
23
  buildSessionSearchParams,
24
24
  getPreciseLocationData,
25
+ resolveB2bUnitId,
25
26
  resolveJwtClaims,
26
27
  resolveSellerInRegion,
27
28
  } from '../utils/validateSessionHelpers'
@@ -187,7 +188,7 @@ export const validateSession = async (
187
188
  oldSession.locale
188
189
  )
189
190
  const marketingData = buildMarketingData(params, oldSession.marketingData)
190
- const { isRepresentative, customerId, unitId } = await resolveJwtClaims(
191
+ const { customerId, unitId } = await resolveJwtClaims(
191
192
  clients,
192
193
  headers?.cookie,
193
194
  account
@@ -211,7 +212,7 @@ export const validateSession = async (
211
212
  const publicData = sessionData?.namespaces.public ?? null
212
213
 
213
214
  let contract = null
214
- if (isRepresentative && profile?.id?.value) {
215
+ if (resolveB2bUnitId(authentication, unitId) && profile?.id?.value) {
215
216
  try {
216
217
  contract = await clients.commerce.masterData.getContractById({
217
218
  contractId: profile.id.value,
@@ -262,7 +263,6 @@ export const validateSession = async (
262
263
  * - Contract data: `profile` namespace (those info will be available inside Faststore's Session `person` object)
263
264
  */
264
265
  b2b: buildB2bSession({
265
- isRepresentative,
266
266
  authentication,
267
267
  shopper,
268
268
  publicData,
@@ -191,6 +191,9 @@ type StoreSession {
191
191
  type StoreB2B {
192
192
  customerId: String!
193
193
  isRepresentative: Boolean
194
+ @deprecated(
195
+ reason: "Always true when `b2b` is set: a B2B session is identified by `unitId` and `customerId`. Check that `b2b` is not null instead. Scheduled for removal in the next major."
196
+ )
194
197
  unitName: String
195
198
  unitId: String
196
199
  firstName: String
@@ -0,0 +1,87 @@
1
+ import { NotFoundError } from '../../errors'
2
+ import type { ProductCluster } from '../clients/search/types/ProductSearchResult'
3
+ import type { GraphqlContext } from '../index'
4
+ import type { EnhancedSku } from './enhanceSku'
5
+ import {
6
+ getSegmentExtraFacets,
7
+ parseSegmentCookie,
8
+ } from './intelligentSearchRequest'
9
+
10
+ const PRODUCT_CLUSTER_IDS_KEY = 'productClusterIds'
11
+ const EXCLUDED_PREFIX = 'not:'
12
+
13
+ export interface ProductClusterRestriction {
14
+ allowed: Set<string>
15
+ excluded: Set<string>
16
+ }
17
+
18
+ /**
19
+ * Collections the shopper's assortment is restricted to, as carried by the
20
+ * `vtex_segment` cookie (`productClusterIds=X` allows, `productClusterIds=not:Y`
21
+ * excludes). Returns `null` when the segment carries no restriction.
22
+ */
23
+ export function getProductClusterRestriction(
24
+ segment: Record<string, unknown>
25
+ ): ProductClusterRestriction | null {
26
+ const allowed = new Set<string>()
27
+ const excluded = new Set<string>()
28
+
29
+ for (const { key, value } of getSegmentExtraFacets(segment)) {
30
+ if (key !== PRODUCT_CLUSTER_IDS_KEY) continue
31
+
32
+ for (const rawId of value.split(',')) {
33
+ const id = rawId.trim()
34
+
35
+ if (id.startsWith(EXCLUDED_PREFIX)) {
36
+ const excludedId = id.slice(EXCLUDED_PREFIX.length)
37
+ if (excludedId) excluded.add(excludedId)
38
+ } else if (id) {
39
+ allowed.add(id)
40
+ }
41
+ }
42
+ }
43
+
44
+ if (allowed.size === 0 && excluded.size === 0) {
45
+ return null
46
+ }
47
+
48
+ return { allowed, excluded }
49
+ }
50
+
51
+ export function isProductInAssortment(
52
+ productClusters: ProductCluster[] | undefined,
53
+ { allowed, excluded }: ProductClusterRestriction
54
+ ): boolean {
55
+ const clusterIds = (productClusters ?? []).map(({ id }) => String(id))
56
+
57
+ if (clusterIds.some((id) => excluded.has(id))) {
58
+ return false
59
+ }
60
+
61
+ return allowed.size === 0 || clusterIds.some((id) => allowed.has(id))
62
+ }
63
+
64
+ /**
65
+ * Listing endpoints get the assortment applied by Intelligent Search through
66
+ * the segment facets, but the `products` endpoint used to fetch a single
67
+ * product does not, so a product outside the shopper's assortment must be
68
+ * rejected here.
69
+ */
70
+ export function assertProductInContractAssortment(
71
+ ctx: GraphqlContext,
72
+ sku: EnhancedSku
73
+ ): void {
74
+ const restriction = getProductClusterRestriction(
75
+ parseSegmentCookie(ctx.headers?.cookie)
76
+ )
77
+
78
+ if (!restriction) {
79
+ return
80
+ }
81
+
82
+ if (!isProductInAssortment(sku.isVariantOf.productClusters, restriction)) {
83
+ throw new NotFoundError(
84
+ `Product ${sku.isVariantOf.productId} is not in the shopper's assortment`
85
+ )
86
+ }
87
+ }
@@ -225,6 +225,16 @@ function extractSegmentData(segment: Record<string, unknown>): {
225
225
  }
226
226
  }
227
227
 
228
+ /**
229
+ * Non-shipping facets carried by the `vtex_segment` cookie (e.g. the B2B
230
+ * contract's `productClusterIds`), in the order they appear in the cookie.
231
+ */
232
+ export function getSegmentExtraFacets(
233
+ segment: Record<string, unknown>
234
+ ): IntelligentSearchFacet[] {
235
+ return extractSegmentData(segment).extraFacets
236
+ }
237
+
228
238
  function appendSegmentParams(
229
239
  params: URLSearchParams,
230
240
  segmentParams: SegmentParams
@@ -107,7 +107,6 @@ export async function resolveJwtClaims(
107
107
  }
108
108
 
109
109
  return {
110
- isRepresentative: isValidJwt ? Boolean(jwt?.isRepresentative) : false,
111
110
  customerId: isValidJwt ? jwt?.customerId : undefined,
112
111
  unitId: isValidJwt ? jwt?.unitId : undefined,
113
112
  }
@@ -140,8 +139,43 @@ function shopperString(value: { value?: unknown } | null | undefined): string {
140
139
  return typeof value?.value === 'string' ? value.value : ''
141
140
  }
142
141
 
142
+ /**
143
+ * First value that is a non-blank string, trimmed. Session values and JWT
144
+ * claims are only nominally typed, so anything else is skipped.
145
+ */
146
+ export function firstNonBlank(...values: unknown[]): string {
147
+ for (const value of values) {
148
+ const trimmed = typeof value === 'string' ? value.trim() : ''
149
+ if (trimmed) return trimmed
150
+ }
151
+
152
+ return ''
153
+ }
154
+
155
+ /**
156
+ * Organizational unit of the shopper, from the session or the JWT claim.
157
+ * Buyer Portal identifies its buyers by the unit; `isRepresentative` alone is
158
+ * not enough, since it can be true outside B2B.
159
+ */
160
+ export function resolveB2bUnitId(
161
+ authentication: { unitId?: { value?: string | null } | null } | null,
162
+ jwtUnitId?: unknown
163
+ ): string {
164
+ return firstNonBlank(authentication?.unitId?.value, jwtUnitId)
165
+ }
166
+
167
+ /**
168
+ * Returns `null` unless the shopper has both an organizational unit and a
169
+ * contract, the rule Buyer Portal uses for a B2B session.
170
+ *
171
+ * The contract falls back from the session's active contract to the session's
172
+ * authentication namespace and then to the JWT claim. Blank values fall
173
+ * through, so an empty session value never hides the claim.
174
+ *
175
+ * The deprecated `isRepresentative` field stays `true` whenever `b2b` is
176
+ * returned, so consumers that still read it keep working.
177
+ */
143
178
  export function buildB2bSession(args: {
144
- isRepresentative: boolean
145
179
  authentication: {
146
180
  customerId?: { value?: string | null } | null
147
181
  unitName?: { value?: string | null } | null
@@ -152,6 +186,7 @@ export function buildB2bSession(args: {
152
186
  firstName?: { value?: unknown } | null
153
187
  lastName?: { value?: unknown } | null
154
188
  organizationManager?: { value?: boolean | null } | null
189
+ activeContractId?: { value?: string | null } | null
155
190
  } | null
156
191
  publicData: { postalCode?: { value?: string | null } | null } | null
157
192
  profile: Parameters<typeof resolveActiveContractDisplayName>[1]
@@ -159,7 +194,14 @@ export function buildB2bSession(args: {
159
194
  customerId?: string
160
195
  unitId?: string
161
196
  }) {
162
- if (!args.isRepresentative) {
197
+ const unitId = resolveB2bUnitId(args.authentication, args.unitId)
198
+ const customerId = firstNonBlank(
199
+ args.shopper?.activeContractId?.value,
200
+ args.authentication?.customerId?.value,
201
+ args.customerId
202
+ )
203
+
204
+ if (!unitId || !customerId) {
163
205
  return null
164
206
  }
165
207
 
@@ -168,9 +210,9 @@ export function buildB2bSession(args: {
168
210
 
169
211
  return {
170
212
  isRepresentative: true,
171
- customerId: args.authentication?.customerId?.value ?? args.customerId ?? '',
213
+ customerId,
172
214
  unitName: args.authentication?.unitName?.value ?? '',
173
- unitId: args.authentication?.unitId?.value ?? args.unitId ?? '',
215
+ unitId,
174
216
  firstName,
175
217
  lastName,
176
218
  userName: `${firstName} ${lastName}`.trim(),