@reactionary/medusa 0.10.54 → 0.10.55

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.
@@ -61,17 +61,17 @@ class MedusaCheckoutCapability extends CheckoutCapability {
61
61
  *
62
62
  * example: this.includedFields = [includedFields, '+discounts.*'].join(',');
63
63
  */
64
- includedFields = ["+items.*", "+order.id", "+order.display_id", "+order.custom_display_id"].join(",");
64
+ includedFields = ["+items.*", "+order.id", "+order.display_id", "+order.custom_display_id", "+shipping_methods.data"].join(",");
65
65
  /**
66
66
  * This controls which fields are requested for the cart's shipping options.
67
67
  * You can override this in a subclass to add more fields as needed.
68
68
  *
69
69
  * The service zone's fulfillment set tells us whether an option is in-store
70
- * pickup (BOPIS) or a delivery, and the option metadata lets medusa-side
71
- * providers push additional data that is mapped to the shipping method's
72
- * protocolData.
70
+ * pickup (BOPIS) or a delivery, and the option data is the fulfillment
71
+ * provider's payload (e.g. nShift option/carrier ids) that is mapped to the
72
+ * shipping method's protocolData.
73
73
  */
74
- shippingOptionFields = ["*service_zone.fulfillment_set", "+metadata"].join(",");
74
+ shippingOptionFields = ["*service_zone.fulfillment_set", "+data"].join(",");
75
75
  initiateCheckoutForCartPayload(payload) {
76
76
  return {
77
77
  billing_address: payload.billingAddress ? this.mapAddressToStoreAddress(payload.billingAddress) : void 0,
@@ -276,12 +276,19 @@ class MedusaCheckoutCapability extends CheckoutCapability {
276
276
  throw new Error("Method not implemented.");
277
277
  }
278
278
  setShippingInstructionPayload(payload) {
279
+ const data = {
280
+ consent_for_unattended_delivery: payload.shippingInstruction.consentForUnattendedDelivery + "",
281
+ instructions: payload.shippingInstruction.instructions || ""
282
+ };
283
+ for (const entry of payload.shippingInstruction.protocolData ?? []) {
284
+ data[entry.key] = entry.value;
285
+ }
286
+ if (payload.shippingInstruction.pickupPoint) {
287
+ data["pickup_point"] = payload.shippingInstruction.pickupPoint;
288
+ }
279
289
  return {
280
290
  option_id: payload.shippingInstruction.shippingMethod.key,
281
- data: {
282
- consent_for_unattended_delivery: payload.shippingInstruction.consentForUnattendedDelivery + "",
283
- instructions: payload.shippingInstruction.instructions || ""
284
- }
291
+ data
285
292
  };
286
293
  }
287
294
  /**
@@ -3,6 +3,7 @@ import {
3
3
  import {
4
4
  AddressIdentifierSchema,
5
5
  MonetaryAmountSchema,
6
+ SHIPPING_PROTOCOL_DATA_KEYS,
6
7
  ShippingMethodIdentifierSchema
7
8
  } from "@reactionary/core";
8
9
  import {
@@ -121,7 +122,7 @@ class MedusaCheckoutFactory {
121
122
  instructions = sm.data["instructions"] != null ? String(sm.data["instructions"]) : "";
122
123
  consentForUnattendedDelivery = sm.data["consent_for_unattended_delivery"] === "true";
123
124
  pickupStore = sm.data["pickup_store"] != null && sm.data["pickup_store"] !== "" ? String(sm.data["pickup_store"]) : void 0;
124
- protocolData = this.parseShippingProtocolData(sm.data["shipping_protocol_data"]);
125
+ protocolData = this.parseInstructionProtocolData(sm.data);
125
126
  }
126
127
  if (!pickupPoint) {
127
128
  pickupPoint = backupPickupPoint || "";
@@ -209,11 +210,46 @@ class MedusaCheckoutFactory {
209
210
  currency: data.calculated_price.currency_code?.toUpperCase() || _context.languageContext.currencyCode
210
211
  }),
211
212
  deliveryTime: "",
213
+ carrier: this.parseShippingMethodCarrier(_context, data),
212
214
  fulfillmentType: this.parseFulfillmentType(_context, data),
213
- protocolData: this.parseShippingMethodProtocolData(_context, data)
215
+ protocolData: this.mergeShippingMethodProtocolData(
216
+ this.parseShippingMethodProtocolData(_context, data),
217
+ this.parseShippingMethodStoreReference(_context, data)
218
+ )
214
219
  };
215
220
  return this.shippingMethodSchema.parse(sm);
216
221
  }
222
+ /**
223
+ * Appends a conventional entry to the provider-derived protocol data,
224
+ * unless the provider data already carries the same key (explicit provider
225
+ * data wins).
226
+ */
227
+ mergeShippingMethodProtocolData(protocolData, entry) {
228
+ if (!entry) {
229
+ return protocolData;
230
+ }
231
+ if ((protocolData ?? []).some((existing) => existing.key === entry.key)) {
232
+ return protocolData;
233
+ }
234
+ return [...protocolData ?? [], entry];
235
+ }
236
+ /**
237
+ * Extension point emitting the conventional "pickup_store" protocol data
238
+ * entry for in-store pickup options: the stock location id of the service
239
+ * zone's fulfillment set, which equals the store capability's
240
+ * Store.identifier.key. Delivery options also carry a location (the
241
+ * warehouse), so the entry is emitted for pickup options only.
242
+ */
243
+ parseShippingMethodStoreReference(_context, data) {
244
+ if (this.parseFulfillmentType(_context, data) !== "pickup") {
245
+ return void 0;
246
+ }
247
+ const locationId = data.service_zone?.fulfillment_set?.location?.id;
248
+ if (!locationId) {
249
+ return void 0;
250
+ }
251
+ return { key: SHIPPING_PROTOCOL_DATA_KEYS.pickupStore, value: locationId };
252
+ }
217
253
  /**
218
254
  * Extension point to control how a Medusa shipping option maps to the core
219
255
  * fulfillment type. A shipping option belonging to a fulfillment set of type
@@ -223,18 +259,33 @@ class MedusaCheckoutFactory {
223
259
  return data.service_zone?.fulfillment_set?.type === "pickup" ? "pickup" : "delivery";
224
260
  }
225
261
  /**
226
- * Extension point mapping a Medusa shipping option's metadata to the
227
- * shipping method's protocolData, so medusa-side providers can push
228
- * additional data (e.g. available pickup time slots) that reaches the
229
- * client. String and JSON-object values are kept as-is, other primitives
230
- * are stringified, and arrays/null entries are skipped.
262
+ * Extension point mapping a Medusa shipping option's `data` - the payload
263
+ * the fulfillment provider works with (e.g. nShift's option/carrier ids) -
264
+ * to the shipping method's protocolData, so provider-protocol data reaches
265
+ * the client. String and JSON-object values are kept as-is, other
266
+ * primitives are stringified, and arrays/null entries are skipped.
267
+ *
268
+ * Option `metadata` is deliberately NOT mapped: it is merchant free-form
269
+ * annotation, not protocol data. Override this hook to surface it.
231
270
  */
232
271
  parseShippingMethodProtocolData(_context, data) {
233
- if (!data.metadata) {
272
+ if (!data.data) {
234
273
  return void 0;
235
274
  }
275
+ const entries = this.convertToProtocolDataEntries(data.data);
276
+ return entries.length > 0 ? entries : void 0;
277
+ }
278
+ /**
279
+ * Converts a Medusa key-value record into protocol data entries: string and
280
+ * JSON-object values are kept as-is, other primitives are stringified, and
281
+ * arrays/null entries are skipped. Keys listed in skipKeys are left out.
282
+ */
283
+ convertToProtocolDataEntries(record, skipKeys = []) {
236
284
  const entries = [];
237
- for (const [key, value] of Object.entries(data.metadata)) {
285
+ for (const [key, value] of Object.entries(record)) {
286
+ if (skipKeys.includes(key)) {
287
+ continue;
288
+ }
238
289
  if (typeof value === "string") {
239
290
  entries.push({ key, value });
240
291
  } else if (typeof value === "number" || typeof value === "boolean") {
@@ -243,6 +294,49 @@ class MedusaCheckoutFactory {
243
294
  entries.push({ key, value });
244
295
  }
245
296
  }
297
+ return entries;
298
+ }
299
+ /**
300
+ * Extension point resolving the carrier of a shipping option from its
301
+ * provider data (e.g. nShift's carrier_id), when present.
302
+ */
303
+ parseShippingMethodCarrier(_context, data) {
304
+ const carrierId = data.data?.["carrier_id"];
305
+ return typeof carrierId === "string" && carrierId.length > 0 ? carrierId : void 0;
306
+ }
307
+ /**
308
+ * The persisted shipping method data keys that are NOT surfaced as
309
+ * instruction protocol data: keys already represented as first-class
310
+ * instruction fields, the legacy JSON blob, and provider-side values that
311
+ * would mislead (nShift's data.price is not the charged amount).
312
+ */
313
+ instructionProtocolDataSkipKeys = [
314
+ "pickup_store",
315
+ "pickup_point",
316
+ "instructions",
317
+ "consent_for_unattended_delivery",
318
+ "shipping_protocol_data",
319
+ "price",
320
+ "currency_code"
321
+ ];
322
+ /**
323
+ * Builds the shipping instruction's protocolData from the persisted
324
+ * shipping method data: the provider payload enriched by the backend (e.g.
325
+ * nShift's session_id, carrier ids and optional delivery_time) merged with
326
+ * whatever the client sent when setting the instruction. First-class and
327
+ * misleading keys are skipped; the legacy shipping_protocol_data JSON blob
328
+ * still contributes entries whose keys are not already present.
329
+ */
330
+ parseInstructionProtocolData(smData) {
331
+ const entries = this.convertToProtocolDataEntries(
332
+ smData,
333
+ this.instructionProtocolDataSkipKeys
334
+ );
335
+ for (const legacyEntry of this.parseShippingProtocolData(smData["shipping_protocol_data"]) ?? []) {
336
+ if (!entries.some((entry) => entry.key === legacyEntry.key)) {
337
+ entries.push(legacyEntry);
338
+ }
339
+ }
246
340
  return entries.length > 0 ? entries : void 0;
247
341
  }
248
342
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactionary/medusa",
3
- "version": "0.10.54",
3
+ "version": "0.10.55",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./src/index.d.ts",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@medusajs/js-sdk": "^2.13.3",
13
- "@reactionary/core": "0.10.54",
13
+ "@reactionary/core": "0.10.55",
14
14
  "debug": "^4.4.3",
15
15
  "@medusajs/types": "^2.13.3",
16
16
  "zod": "4.1.9"
@@ -25,9 +25,9 @@ export declare class MedusaCheckoutCapability<TFactory extends CheckoutFactory =
25
25
  * You can override this in a subclass to add more fields as needed.
26
26
  *
27
27
  * The service zone's fulfillment set tells us whether an option is in-store
28
- * pickup (BOPIS) or a delivery, and the option metadata lets medusa-side
29
- * providers push additional data that is mapped to the shipping method's
30
- * protocolData.
28
+ * pickup (BOPIS) or a delivery, and the option data is the fulfillment
29
+ * provider's payload (e.g. nShift option/carrier ids) that is mapped to the
30
+ * shipping method's protocolData.
31
31
  */
32
32
  protected shippingOptionFields: string;
33
33
  constructor(config: MedusaConfiguration, cache: Cache, context: RequestContext, medusaApi: MedusaAPI, factory: CheckoutFactoryWithOutput<TFactory>);
@@ -45,6 +45,20 @@ export declare class MedusaCheckoutFactory<TCheckoutSchema extends AnyCheckoutSc
45
45
  protected isCart(data: StoreCart): data is StoreCart;
46
46
  parseCheckout(context: RequestContext, data: StoreCart): z.output<TCheckoutSchema>;
47
47
  parseShippingMethod(_context: RequestContext, data: MedusaShippingOptionWithMetadata): z.output<TShippingMethodSchema>;
48
+ /**
49
+ * Appends a conventional entry to the provider-derived protocol data,
50
+ * unless the provider data already carries the same key (explicit provider
51
+ * data wins).
52
+ */
53
+ protected mergeShippingMethodProtocolData(protocolData: ShippingProtocolData[] | undefined, entry: ShippingProtocolData | undefined): ShippingProtocolData[] | undefined;
54
+ /**
55
+ * Extension point emitting the conventional "pickup_store" protocol data
56
+ * entry for in-store pickup options: the stock location id of the service
57
+ * zone's fulfillment set, which equals the store capability's
58
+ * Store.identifier.key. Delivery options also carry a location (the
59
+ * warehouse), so the entry is emitted for pickup options only.
60
+ */
61
+ protected parseShippingMethodStoreReference(_context: RequestContext, data: MedusaShippingOptionWithMetadata): ShippingProtocolData | undefined;
48
62
  /**
49
63
  * Extension point to control how a Medusa shipping option maps to the core
50
64
  * fulfillment type. A shipping option belonging to a fulfillment set of type
@@ -52,13 +66,43 @@ export declare class MedusaCheckoutFactory<TCheckoutSchema extends AnyCheckoutSc
52
66
  */
53
67
  protected parseFulfillmentType(_context: RequestContext, data: MedusaShippingOptionWithMetadata): ShippingFulfillmentType;
54
68
  /**
55
- * Extension point mapping a Medusa shipping option's metadata to the
56
- * shipping method's protocolData, so medusa-side providers can push
57
- * additional data (e.g. available pickup time slots) that reaches the
58
- * client. String and JSON-object values are kept as-is, other primitives
59
- * are stringified, and arrays/null entries are skipped.
69
+ * Extension point mapping a Medusa shipping option's `data` - the payload
70
+ * the fulfillment provider works with (e.g. nShift's option/carrier ids) -
71
+ * to the shipping method's protocolData, so provider-protocol data reaches
72
+ * the client. String and JSON-object values are kept as-is, other
73
+ * primitives are stringified, and arrays/null entries are skipped.
74
+ *
75
+ * Option `metadata` is deliberately NOT mapped: it is merchant free-form
76
+ * annotation, not protocol data. Override this hook to surface it.
60
77
  */
61
78
  protected parseShippingMethodProtocolData(_context: RequestContext, data: MedusaShippingOptionWithMetadata): ShippingProtocolData[] | undefined;
79
+ /**
80
+ * Converts a Medusa key-value record into protocol data entries: string and
81
+ * JSON-object values are kept as-is, other primitives are stringified, and
82
+ * arrays/null entries are skipped. Keys listed in skipKeys are left out.
83
+ */
84
+ protected convertToProtocolDataEntries(record: Record<string, unknown>, skipKeys?: readonly string[]): ShippingProtocolData[];
85
+ /**
86
+ * Extension point resolving the carrier of a shipping option from its
87
+ * provider data (e.g. nShift's carrier_id), when present.
88
+ */
89
+ protected parseShippingMethodCarrier(_context: RequestContext, data: MedusaShippingOptionWithMetadata): string | undefined;
90
+ /**
91
+ * The persisted shipping method data keys that are NOT surfaced as
92
+ * instruction protocol data: keys already represented as first-class
93
+ * instruction fields, the legacy JSON blob, and provider-side values that
94
+ * would mislead (nShift's data.price is not the charged amount).
95
+ */
96
+ protected instructionProtocolDataSkipKeys: readonly string[];
97
+ /**
98
+ * Builds the shipping instruction's protocolData from the persisted
99
+ * shipping method data: the provider payload enriched by the backend (e.g.
100
+ * nShift's session_id, carrier ids and optional delivery_time) merged with
101
+ * whatever the client sent when setting the instruction. First-class and
102
+ * misleading keys are skipped; the legacy shipping_protocol_data JSON blob
103
+ * still contributes entries whose keys are not already present.
104
+ */
105
+ protected parseInstructionProtocolData(smData: Record<string, unknown>): ShippingProtocolData[] | undefined;
62
106
  /**
63
107
  * Parses shipping protocol data persisted as a JSON object string (keys
64
108
  * mapping to string or JSON-object values) back into protocol data entries.