@reactionary/provider-medusa 0.2.1 → 0.2.3

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.
@@ -1,14 +1,17 @@
1
1
  import { MedusaCartProvider } from "../providers/cart.provider.js";
2
+ import { MedusaCategoryProvider } from "../providers/category.provider.js";
3
+ import { MedusaCheckoutProvider } from "../providers/checkout.provider.js";
2
4
  import { MedusaIdentityProvider } from "../providers/identity.provider.js";
3
5
  import { MedusaInventoryProvider } from "../providers/inventory.provider.js";
6
+ import { MedusaOrderSearchProvider } from "../providers/order-search.provider.js";
7
+ import { MedusaOrderProvider } from "../providers/order.provider.js";
4
8
  import { MedusaPriceProvider } from "../providers/price.provider.js";
5
- import { MedusaCapabilitiesSchema } from "../schema/capabilities.schema.js";
6
- import { MedusaConfigurationSchema } from "../schema/configuration.schema.js";
7
9
  import { MedusaSearchProvider } from "../providers/product-search.provider.js";
8
10
  import { MedusaProductProvider } from "../providers/product.provider.js";
11
+ import { MedusaProfileProvider } from "../providers/profile.provider.js";
12
+ import { MedusaCapabilitiesSchema } from "../schema/capabilities.schema.js";
13
+ import { MedusaConfigurationSchema } from "../schema/configuration.schema.js";
9
14
  import { MedusaClient } from "./client.js";
10
- import { MedusaCategoryProvider } from "../providers/category.provider.js";
11
- import { MedusaCheckoutProvider } from "../providers/checkout.provider.js";
12
15
  function withMedusaCapabilities(configuration, capabilities) {
13
16
  return (cache, context) => {
14
17
  const client = {};
@@ -39,6 +42,15 @@ function withMedusaCapabilities(configuration, capabilities) {
39
42
  if (caps.identity) {
40
43
  client.identity = new MedusaIdentityProvider(configuration, cache, context, medusaClient);
41
44
  }
45
+ if (caps.profile) {
46
+ client.profile = new MedusaProfileProvider(configuration, cache, context, medusaClient);
47
+ }
48
+ if (caps.order) {
49
+ client.order = new MedusaOrderProvider(configuration, cache, context, medusaClient);
50
+ }
51
+ if (caps.orderSearch) {
52
+ client.orderSearch = new MedusaOrderSearchProvider(configuration, cache, context, medusaClient);
53
+ }
42
54
  return client;
43
55
  };
44
56
  }
package/index.js CHANGED
@@ -6,5 +6,8 @@ export * from "./core/client.js";
6
6
  export * from "./providers/cart.provider.js";
7
7
  export * from "./providers/identity.provider.js";
8
8
  export * from "./providers/inventory.provider.js";
9
+ export * from "./providers/order.provider.js";
10
+ export * from "./providers/order-search.provider.js";
9
11
  export * from "./providers/price.provider.js";
10
12
  export * from "./providers/product-search.provider.js";
13
+ export * from "./providers/profile.provider.js";
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@reactionary/provider-medusa",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "src/index.d.ts",
7
7
  "dependencies": {
8
8
  "zod": "4.1.9",
9
- "@reactionary/core": "0.2.1",
9
+ "@reactionary/core": "0.2.3",
10
10
  "@medusajs/js-sdk": "^2.0.0",
11
11
  "debug": "^4.3.4",
12
12
  "@medusajs/types": "^2.11.0",
@@ -0,0 +1,157 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ AddressIdentifierSchema,
14
+ OrderSearchProvider,
15
+ OrderSearchQueryByTermSchema,
16
+ OrderSearchResultSchema,
17
+ Reactionary,
18
+ success
19
+ } from "@reactionary/core";
20
+ import createDebug from "debug";
21
+ const debug = createDebug("reactionary:medusa:order-search");
22
+ class MedusaOrderSearchProvider extends OrderSearchProvider {
23
+ constructor(config, cache, context, client) {
24
+ super(cache, context);
25
+ this.config = config;
26
+ this.client = client;
27
+ }
28
+ async queryByTerm(payload) {
29
+ debug("queryByTerm", payload);
30
+ const medusa = await this.client.getClient();
31
+ if (payload.search.term) {
32
+ debug("Searching orders by term is not supported in Medusa");
33
+ }
34
+ if (payload.search.partNumber) {
35
+ debug("Searching orders by part number is not supported in Medusa");
36
+ }
37
+ if (payload.search.startDate) {
38
+ debug("Searching orders by start date is not supported in Medusa");
39
+ }
40
+ if (payload.search.endDate) {
41
+ debug("Searching orders by end date is not supported in Medusa");
42
+ }
43
+ if (payload.search.userId) {
44
+ debug("Searching orders by customer ID is not supported in Medusa");
45
+ }
46
+ const statusFilter = (payload.search.orderStatus ?? []).map((status) => {
47
+ let retStatus = "draft";
48
+ if (status === "AwaitingPayment") {
49
+ retStatus = "draft";
50
+ }
51
+ if (status === "ReleasedToFulfillment") {
52
+ retStatus = "pending";
53
+ }
54
+ if (status === "Shipped") {
55
+ retStatus = "completed";
56
+ }
57
+ if (status === "Cancelled") {
58
+ retStatus = "canceled";
59
+ }
60
+ return retStatus;
61
+ });
62
+ const response = await medusa.store.order.list({
63
+ status: statusFilter,
64
+ limit: payload.search.paginationOptions.pageSize,
65
+ offset: (payload.search.paginationOptions.pageNumber - 1) * payload.search.paginationOptions.pageSize
66
+ });
67
+ const result = this.parsePaginatedResult(response, payload);
68
+ if (debug.enabled) {
69
+ debug(
70
+ `Search for term "${payload.search.term}" returned ${response.orders.length} orders (page ${payload.search.paginationOptions.pageNumber} of ${result.totalPages})`
71
+ );
72
+ }
73
+ return success(result);
74
+ }
75
+ composeAddressFromStoreAddress(storeAddress) {
76
+ return {
77
+ identifier: AddressIdentifierSchema.parse({
78
+ nickName: storeAddress.id
79
+ }),
80
+ firstName: storeAddress.first_name || "",
81
+ lastName: storeAddress.last_name || "",
82
+ streetAddress: storeAddress.address_1 || "",
83
+ streetNumber: storeAddress.address_2 || "",
84
+ city: storeAddress.city || "",
85
+ postalCode: storeAddress.postal_code || "",
86
+ countryCode: storeAddress.country_code || "",
87
+ region: ""
88
+ };
89
+ }
90
+ parseSingle(body) {
91
+ const identifier = { key: body.id };
92
+ const userId = {
93
+ userId: body.customer_id || ""
94
+ };
95
+ const customerName = `${body.billing_address?.first_name} ${body.billing_address?.last_name}`;
96
+ const shippingAddress = this.composeAddressFromStoreAddress(body.shipping_address);
97
+ const orderDate = new Date(body.created_at).toISOString();
98
+ let orderStatus = "AwaitingPayment";
99
+ if (body.status === "draft") {
100
+ orderStatus = "AwaitingPayment";
101
+ }
102
+ if (body.status === "pending") {
103
+ orderStatus = "ReleasedToFulfillment";
104
+ }
105
+ if (body.status === "completed") {
106
+ orderStatus = "Shipped";
107
+ }
108
+ if (body.status === "canceled") {
109
+ orderStatus = "Cancelled";
110
+ }
111
+ let inventoryStatus = "NotAllocated";
112
+ if (body.fulfillment_status === "fulfilled") {
113
+ inventoryStatus = "Allocated";
114
+ }
115
+ const totalAmount = {
116
+ currency: body.currency_code.toUpperCase(),
117
+ value: body.total ? body.total : 0
118
+ };
119
+ const order = {
120
+ identifier,
121
+ userId,
122
+ customerName,
123
+ shippingAddress,
124
+ orderDate,
125
+ orderStatus,
126
+ inventoryStatus,
127
+ totalAmount
128
+ };
129
+ return order;
130
+ }
131
+ parsePaginatedResult(body, query) {
132
+ const identifier = {
133
+ ...query.search
134
+ };
135
+ const orders = body.orders.map((o) => {
136
+ return this.parseSingle(o);
137
+ });
138
+ const result = {
139
+ identifier,
140
+ pageNumber: (Math.ceil(body.offset / body.limit) || 0) + 1,
141
+ pageSize: body.limit,
142
+ totalCount: body.count,
143
+ totalPages: Math.ceil(body.count / body.limit || 0) + 1,
144
+ items: orders
145
+ };
146
+ return result;
147
+ }
148
+ }
149
+ __decorateClass([
150
+ Reactionary({
151
+ inputSchema: OrderSearchQueryByTermSchema,
152
+ outputSchema: OrderSearchResultSchema
153
+ })
154
+ ], MedusaOrderSearchProvider.prototype, "queryByTerm", 1);
155
+ export {
156
+ MedusaOrderSearchProvider
157
+ };
@@ -0,0 +1,175 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ OrderProvider,
14
+ OrderQueryByIdSchema,
15
+ OrderSchema,
16
+ Reactionary,
17
+ success,
18
+ error,
19
+ ProductVariantIdentifierSchema
20
+ } from "@reactionary/core";
21
+ import createDebug from "debug";
22
+ import { handleProviderError } from "../utils/medusa-helpers.js";
23
+ import { parseMedusaItemPrice, parseMedusaCostBreakdown } from "../utils/medusa-helpers.js";
24
+ const debug = createDebug("reactionary:medusa:order");
25
+ class MedusaOrderProvider extends OrderProvider {
26
+ constructor(config, cache, context, client) {
27
+ super(cache, context);
28
+ this.config = config;
29
+ this.client = client;
30
+ }
31
+ async getById(payload) {
32
+ debug("getById", payload);
33
+ const medusa = await this.client.getClient();
34
+ try {
35
+ const response = await medusa.store.order.retrieve(payload.order.key);
36
+ const order = this.parseSingle(response.order);
37
+ return success(order);
38
+ } catch (err) {
39
+ return handleProviderError("order", err);
40
+ }
41
+ }
42
+ /**
43
+ * Extension point to control the parsing of a single cart item price
44
+ * @param remoteItem
45
+ * @param currency
46
+ * @returns
47
+ */
48
+ parseItemPrice(remoteItem, currency) {
49
+ return parseMedusaItemPrice(remoteItem, currency);
50
+ }
51
+ /**
52
+ * Extension point to control the parsing of the cost breakdown of a cart
53
+ * @param remote
54
+ * @returns
55
+ */
56
+ parseCostBreakdown(remote) {
57
+ return parseMedusaCostBreakdown(remote);
58
+ }
59
+ /**
60
+ * Extension point to control the parsing of a single cart item
61
+ * @param remoteItem
62
+ * @param currency
63
+ * @returns
64
+ */
65
+ parseOrderItem(remoteItem, currency) {
66
+ const item = {
67
+ identifier: {
68
+ key: remoteItem.id
69
+ },
70
+ variant: ProductVariantIdentifierSchema.parse({
71
+ sku: remoteItem.variant_sku || ""
72
+ }),
73
+ quantity: remoteItem.quantity || 1,
74
+ price: this.parseItemPrice(remoteItem, currency),
75
+ inventoryStatus: "Allocated"
76
+ };
77
+ return item;
78
+ }
79
+ parsePaymentInstruction(remotePayment, order) {
80
+ const paymentMethodIdentifier = {
81
+ method: remotePayment.payment_providers?.[0]?.id || "unknown",
82
+ name: remotePayment.payment_providers?.[0]?.id || "unknown",
83
+ paymentProcessor: remotePayment.payment_providers?.[0]?.id || "unknown"
84
+ };
85
+ let status = "pending";
86
+ switch (remotePayment.status) {
87
+ case "not_paid":
88
+ status = "pending";
89
+ break;
90
+ case "awaiting":
91
+ status = "pending";
92
+ break;
93
+ case "authorized":
94
+ status = "authorized";
95
+ break;
96
+ case "partially_authorized":
97
+ status = "pending";
98
+ break;
99
+ case "canceled":
100
+ status = "canceled";
101
+ break;
102
+ case "failed":
103
+ status = "canceled";
104
+ break;
105
+ case "completed":
106
+ status = "capture";
107
+ break;
108
+ }
109
+ const paymentData = remotePayment.payments?.[0].data || {};
110
+ const pi = {
111
+ identifier: {
112
+ key: remotePayment.id
113
+ },
114
+ amount: {
115
+ value: remotePayment.amount,
116
+ currency: remotePayment.currency_code?.toUpperCase()
117
+ },
118
+ paymentMethod: paymentMethodIdentifier,
119
+ protocolData: paymentData ? Object.entries(paymentData).map(([key, value]) => ({
120
+ key,
121
+ value: String(value)
122
+ })) : [],
123
+ status
124
+ };
125
+ return pi;
126
+ }
127
+ parseSingle(body) {
128
+ const identifier = { key: body.id };
129
+ const userId = {
130
+ userId: body.customer_id || ""
131
+ };
132
+ const items = (body.items || []).map((item) => {
133
+ return this.parseOrderItem(item, body.currency_code.toUpperCase());
134
+ });
135
+ const price = this.parseCostBreakdown(body);
136
+ let orderStatus = "AwaitingPayment";
137
+ if (body.status === "draft") {
138
+ orderStatus = "AwaitingPayment";
139
+ }
140
+ if (body.status === "pending") {
141
+ orderStatus = "ReleasedToFulfillment";
142
+ }
143
+ if (body.status === "completed") {
144
+ orderStatus = "Shipped";
145
+ }
146
+ if (body.status === "canceled") {
147
+ orderStatus = "Cancelled";
148
+ }
149
+ let inventoryStatus = "NotAllocated";
150
+ if (body.fulfillment_status === "fulfilled") {
151
+ inventoryStatus = "Allocated";
152
+ }
153
+ const paymentInstructions = body.payment_collections?.map((pc) => {
154
+ return this.parsePaymentInstruction(pc, body);
155
+ }) || [];
156
+ return {
157
+ identifier,
158
+ userId,
159
+ items,
160
+ price,
161
+ orderStatus,
162
+ inventoryStatus,
163
+ paymentInstructions
164
+ };
165
+ }
166
+ }
167
+ __decorateClass([
168
+ Reactionary({
169
+ inputSchema: OrderQueryByIdSchema,
170
+ outputSchema: OrderSchema
171
+ })
172
+ ], MedusaOrderProvider.prototype, "getById", 1);
173
+ export {
174
+ MedusaOrderProvider
175
+ };
@@ -0,0 +1,323 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result)
9
+ __defProp(target, key, result);
10
+ return result;
11
+ };
12
+ import {
13
+ ProfileProvider,
14
+ Reactionary,
15
+ ProfileSchema,
16
+ ProfileMutationUpdateSchema,
17
+ ProfileMutationAddShippingAddressSchema,
18
+ ProfileMutationUpdateShippingAddressSchema,
19
+ ProfileMutationRemoveShippingAddressSchema,
20
+ ProfileMutationMakeShippingAddressDefaultSchema,
21
+ ProfileMutationSetBillingAddressSchema,
22
+ success,
23
+ ProfileQueryByIdSchema,
24
+ error
25
+ } from "@reactionary/core";
26
+ import createDebug from "debug";
27
+ const debug = createDebug("reactionary:medusa:profile");
28
+ class MedusaProfileProvider extends ProfileProvider {
29
+ constructor(config, cache, context, client) {
30
+ super(cache, context);
31
+ this.includedFields = ["+metadata.*"];
32
+ this.config = config;
33
+ this.client = client;
34
+ }
35
+ async getById(payload) {
36
+ debug("getById", payload);
37
+ const client = await this.client.getClient();
38
+ const customerResponse = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
39
+ if (!customerResponse.customer) {
40
+ return error({
41
+ identifier: payload.identifier,
42
+ type: "NotFound"
43
+ });
44
+ }
45
+ const model = this.parseSingle(customerResponse.customer);
46
+ return success(model);
47
+ }
48
+ async update(payload) {
49
+ debug("update", payload);
50
+ const client = await this.client.getClient();
51
+ const customerResponse = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
52
+ if (!customerResponse.customer) {
53
+ return error({
54
+ type: "NotFound",
55
+ identifier: payload.identifier
56
+ });
57
+ }
58
+ const customer = customerResponse.customer;
59
+ const updatedResponse = await client.store.customer.update({
60
+ phone: payload.phone ?? customer.phone
61
+ }, { fields: this.includedFields.join(",") });
62
+ const model = this.parseSingle(updatedResponse.customer);
63
+ return success(model);
64
+ }
65
+ async addShippingAddress(payload) {
66
+ debug("addShippingAddress", payload);
67
+ const client = await this.client.getClient();
68
+ const medusaAddress = this.createMedusaAddress(payload.address);
69
+ const customer = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
70
+ if (!customer.customer) {
71
+ return error({
72
+ type: "NotFound",
73
+ identifier: payload.identifier
74
+ });
75
+ }
76
+ const existingAddress = customer.customer.addresses.find((addr) => addr.address_name === payload.address.identifier.nickName);
77
+ if (existingAddress) {
78
+ return error({
79
+ type: "InvalidInput",
80
+ error: {
81
+ message: "Address with the same nickname already exists"
82
+ }
83
+ });
84
+ }
85
+ const response = await client.store.customer.createAddress(medusaAddress, { fields: this.includedFields.join(",") });
86
+ if (!response.customer) {
87
+ return error({
88
+ type: "InvalidInput",
89
+ error: {
90
+ message: "Failed to add shipping address"
91
+ }
92
+ });
93
+ }
94
+ const model = this.parseSingle(response.customer);
95
+ return success(model);
96
+ }
97
+ async updateShippingAddress(payload) {
98
+ debug("updateShippingAddress", payload);
99
+ const client = await this.client.getClient();
100
+ const customer = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
101
+ if (!customer.customer) {
102
+ return error({
103
+ type: "NotFound",
104
+ identifier: payload.identifier
105
+ });
106
+ }
107
+ const medusaAddress = this.createMedusaAddress(payload.address);
108
+ const existingAddress = customer.customer.addresses.find((addr) => addr.address_name === payload.address.identifier.nickName);
109
+ if (!existingAddress) {
110
+ return error({
111
+ type: "NotFound",
112
+ identifier: payload.address.identifier
113
+ });
114
+ }
115
+ const response = await client.store.customer.updateAddress(existingAddress.id, medusaAddress, { fields: this.includedFields.join(",") });
116
+ if (!response.customer) {
117
+ return error({
118
+ type: "InvalidInput",
119
+ error: {
120
+ message: "Failed to add shipping address"
121
+ }
122
+ });
123
+ }
124
+ const model = this.parseSingle(response.customer);
125
+ return success(model);
126
+ }
127
+ async removeShippingAddress(payload) {
128
+ debug("removeShippingAddress", payload);
129
+ const client = await this.client.getClient();
130
+ const customer = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
131
+ if (!customer.customer) {
132
+ return error({
133
+ type: "NotFound",
134
+ identifier: payload.identifier
135
+ });
136
+ }
137
+ const existingAddress = customer.customer.addresses.find((addr) => addr.address_name === payload.addressIdentifier.nickName);
138
+ if (!existingAddress) {
139
+ return error({
140
+ type: "NotFound",
141
+ identifier: payload.addressIdentifier
142
+ });
143
+ }
144
+ const response = await client.store.customer.deleteAddress(existingAddress.id, { fields: this.includedFields.join(",") });
145
+ if (!response.deleted) {
146
+ return error({
147
+ type: "InvalidInput",
148
+ error: {
149
+ message: "Failed to delete shipping address"
150
+ }
151
+ });
152
+ }
153
+ const customerAfterDelete = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
154
+ const model = this.parseSingle(customerAfterDelete.customer);
155
+ return success(model);
156
+ }
157
+ async makeShippingAddressDefault(payload) {
158
+ debug("makeShippingAddressDefault", payload);
159
+ const client = await this.client.getClient();
160
+ const customer = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
161
+ if (!customer.customer) {
162
+ return error({
163
+ type: "NotFound",
164
+ identifier: payload.identifier
165
+ });
166
+ }
167
+ const existingAddress = customer.customer.addresses.find((addr) => addr.address_name === payload.addressIdentifier.nickName);
168
+ if (!existingAddress) {
169
+ return error({
170
+ type: "NotFound",
171
+ identifier: payload.addressIdentifier
172
+ });
173
+ }
174
+ const response = await client.store.customer.updateAddress(
175
+ existingAddress.id,
176
+ {
177
+ is_default_shipping: true
178
+ },
179
+ { fields: this.includedFields.join(",") }
180
+ );
181
+ const model = this.parseSingle(response.customer);
182
+ return success(model);
183
+ }
184
+ async setBillingAddress(payload) {
185
+ debug("setBillingAddress", payload);
186
+ const client = await this.client.getClient();
187
+ const customerResponse = await client.store.customer.retrieve({ fields: this.includedFields.join(",") });
188
+ if (!customerResponse.customer) {
189
+ return error({
190
+ type: "NotFound",
191
+ identifier: payload.identifier
192
+ });
193
+ }
194
+ let customer = customerResponse.customer;
195
+ const existingAddressWithNickname = customer.addresses.find((addr) => addr.address_name === payload.address.identifier.nickName);
196
+ if (existingAddressWithNickname && !existingAddressWithNickname.is_default_billing) {
197
+ return error({
198
+ type: "InvalidInput",
199
+ error: {
200
+ message: "Another address with the same nickname already exists"
201
+ }
202
+ });
203
+ }
204
+ const newAddr = this.createMedusaAddress(payload.address);
205
+ newAddr.is_default_billing = true;
206
+ const existingBillingAddress = customer.addresses.find((addr) => addr.is_default_billing);
207
+ if (existingBillingAddress) {
208
+ const updateAddressResponse = await client.store.customer.updateAddress(existingBillingAddress.id, newAddr, { fields: this.includedFields.join(",") });
209
+ customer = updateAddressResponse.customer;
210
+ } else {
211
+ const createAddressResponse = await client.store.customer.createAddress(newAddr, { fields: this.includedFields.join(",") });
212
+ customer = createAddressResponse.customer;
213
+ }
214
+ const model = this.parseSingle(customer);
215
+ return success(model);
216
+ }
217
+ createMedusaAddress(address) {
218
+ return {
219
+ address_name: address.identifier.nickName,
220
+ first_name: address.firstName,
221
+ last_name: address.lastName,
222
+ address_1: address.streetAddress,
223
+ address_2: address.streetNumber,
224
+ city: address.city,
225
+ province: address.region,
226
+ postal_code: address.postalCode,
227
+ country_code: address.countryCode
228
+ };
229
+ }
230
+ parseAddress(address) {
231
+ return {
232
+ identifier: {
233
+ nickName: address.address_name || ""
234
+ },
235
+ firstName: address.first_name || "",
236
+ lastName: address.last_name || "",
237
+ streetAddress: address.address_1 || "",
238
+ streetNumber: address.address_2 || "",
239
+ city: address.city || "",
240
+ region: address.province || "",
241
+ postalCode: address.postal_code || "",
242
+ countryCode: address.country_code || ""
243
+ };
244
+ }
245
+ parseSingle(customer) {
246
+ const email = customer.email;
247
+ const emailVerified = customer.metadata?.["email_verified"] === "true";
248
+ const phone = customer.phone || "";
249
+ const phoneVerified = customer.metadata?.["phone_verified"] === "true";
250
+ const addresses = customer.addresses || [];
251
+ let billingAddress = void 0;
252
+ let shippingAddress = void 0;
253
+ const existingBillingAddress = customer.addresses.find((addr) => addr.is_default_billing);
254
+ if (existingBillingAddress) {
255
+ billingAddress = this.parseAddress(existingBillingAddress);
256
+ }
257
+ const existingShippingAddress = customer.addresses.find((addr) => addr.is_default_shipping);
258
+ if (existingShippingAddress) {
259
+ shippingAddress = this.parseAddress(existingShippingAddress);
260
+ }
261
+ const alternateShippingAddresses = [];
262
+ alternateShippingAddresses.push(...addresses.filter((x) => !(x.is_default_billing || x.is_default_shipping)).map((addr) => this.parseAddress(addr)));
263
+ return {
264
+ identifier: {
265
+ userId: customer.id
266
+ },
267
+ email,
268
+ emailVerified,
269
+ phone,
270
+ phoneVerified,
271
+ billingAddress,
272
+ shippingAddress,
273
+ alternateShippingAddresses,
274
+ createdAt: new Date(customer.created_at || "").toISOString(),
275
+ updatedAt: new Date(customer.updated_at || "").toISOString()
276
+ };
277
+ }
278
+ }
279
+ __decorateClass([
280
+ Reactionary({
281
+ inputSchema: ProfileQueryByIdSchema,
282
+ outputSchema: ProfileSchema
283
+ })
284
+ ], MedusaProfileProvider.prototype, "getById", 1);
285
+ __decorateClass([
286
+ Reactionary({
287
+ inputSchema: ProfileMutationUpdateSchema,
288
+ outputSchema: ProfileSchema
289
+ })
290
+ ], MedusaProfileProvider.prototype, "update", 1);
291
+ __decorateClass([
292
+ Reactionary({
293
+ inputSchema: ProfileMutationAddShippingAddressSchema,
294
+ outputSchema: ProfileSchema
295
+ })
296
+ ], MedusaProfileProvider.prototype, "addShippingAddress", 1);
297
+ __decorateClass([
298
+ Reactionary({
299
+ inputSchema: ProfileMutationUpdateShippingAddressSchema,
300
+ outputSchema: ProfileSchema
301
+ })
302
+ ], MedusaProfileProvider.prototype, "updateShippingAddress", 1);
303
+ __decorateClass([
304
+ Reactionary({
305
+ inputSchema: ProfileMutationRemoveShippingAddressSchema,
306
+ outputSchema: ProfileSchema
307
+ })
308
+ ], MedusaProfileProvider.prototype, "removeShippingAddress", 1);
309
+ __decorateClass([
310
+ Reactionary({
311
+ inputSchema: ProfileMutationMakeShippingAddressDefaultSchema,
312
+ outputSchema: ProfileSchema
313
+ })
314
+ ], MedusaProfileProvider.prototype, "makeShippingAddressDefault", 1);
315
+ __decorateClass([
316
+ Reactionary({
317
+ inputSchema: ProfileMutationSetBillingAddressSchema,
318
+ outputSchema: ProfileSchema
319
+ })
320
+ ], MedusaProfileProvider.prototype, "setBillingAddress", 1);
321
+ export {
322
+ MedusaProfileProvider
323
+ };
@@ -6,8 +6,11 @@ const MedusaCapabilitiesSchema = CapabilitiesSchema.pick({
6
6
  category: true,
7
7
  product: true,
8
8
  price: true,
9
+ order: true,
10
+ orderSearch: true,
9
11
  inventory: true,
10
- identity: true
12
+ identity: true,
13
+ profile: true
11
14
  }).partial();
12
15
  export {
13
16
  MedusaCapabilitiesSchema
package/src/index.d.ts CHANGED
@@ -6,5 +6,8 @@ export * from './core/client.js';
6
6
  export * from './providers/cart.provider.js';
7
7
  export * from './providers/identity.provider.js';
8
8
  export * from './providers/inventory.provider.js';
9
+ export * from './providers/order.provider.js';
10
+ export * from './providers/order-search.provider.js';
9
11
  export * from './providers/price.provider.js';
10
12
  export * from './providers/product-search.provider.js';
13
+ export * from './providers/profile.provider.js';
@@ -0,0 +1,91 @@
1
+ import type { RequestContext, Cache, OrderSearchQueryByTerm, OrderSearchResult, Result, OrderStatus, Address } from '@reactionary/core';
2
+ import { OrderSearchProvider } from '@reactionary/core';
3
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
4
+ import type { MedusaClient } from '../core/client.js';
5
+ import type { StoreOrder, StoreOrderAddress, StoreOrderListResponse } from '@medusajs/types';
6
+ export declare class MedusaOrderSearchProvider extends OrderSearchProvider {
7
+ protected config: MedusaConfiguration;
8
+ protected client: MedusaClient;
9
+ constructor(config: MedusaConfiguration, cache: Cache, context: RequestContext, client: MedusaClient);
10
+ queryByTerm(payload: OrderSearchQueryByTerm): Promise<Result<OrderSearchResult>>;
11
+ protected composeAddressFromStoreAddress(storeAddress: StoreOrderAddress): Address;
12
+ protected parseSingle(body: StoreOrder): {
13
+ identifier: {
14
+ key: string;
15
+ };
16
+ userId: {
17
+ userId: string;
18
+ };
19
+ customerName: string;
20
+ shippingAddress: {
21
+ identifier: {
22
+ nickName: string;
23
+ };
24
+ firstName: string;
25
+ lastName: string;
26
+ streetAddress: string;
27
+ streetNumber: string;
28
+ city: string;
29
+ region: string;
30
+ postalCode: string;
31
+ countryCode: string;
32
+ };
33
+ orderDate: string;
34
+ orderStatus: OrderStatus;
35
+ inventoryStatus: "NotAllocated" | "Allocated";
36
+ totalAmount: {
37
+ value: number;
38
+ currency: "AED" | "AFN" | "ALL" | "AMD" | "ANG" | "AOA" | "ARS" | "AUD" | "AWG" | "AZN" | "BAM" | "BBD" | "BDT" | "BGN" | "BHD" | "BIF" | "BMD" | "BND" | "BOB" | "BOV" | "BRL" | "BSD" | "BTN" | "BWP" | "BYN" | "BZD" | "CAD" | "CDF" | "CHE" | "CHF" | "CHW" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "CRC" | "CUC" | "CUP" | "CVE" | "CZK" | "DJF" | "DKK" | "DOP" | "DZD" | "EGP" | "ERN" | "ETB" | "EUR" | "FJD" | "FKP" | "GBP" | "GEL" | "GHS" | "GIP" | "GMD" | "GNF" | "GTQ" | "GYD" | "HKD" | "HNL" | "HRK" | "HTG" | "HUF" | "IDR" | "ILS" | "INR" | "IQD" | "IRR" | "ISK" | "JMD" | "JOD" | "JPY" | "KES" | "KGS" | "KHR" | "KMF" | "KPW" | "KRW" | "KWD" | "KYD" | "KZT" | "LAK" | "LBP" | "LKR" | "LRD" | "LSL" | "LYD" | "MAD" | "MDL" | "MGA" | "MKD" | "MMK" | "MNT" | "MOP" | "MRU" | "MUR" | "MVR" | "MWK" | "MXN" | "MXV" | "MYR" | "MZN" | "NAD" | "NGN" | "NIO" | "NOK" | "NPR" | "NZD" | "OMR" | "PAB" | "PEN" | "PGK" | "PHP" | "PKR" | "PLN" | "PYG" | "QAR" | "RON" | "RSD" | "RUB" | "RWF" | "SAR" | "SBD" | "SCR" | "SDG" | "SEK" | "SGD" | "SHP" | "SLE" | "SLL" | "SOS" | "SRD" | "SSP" | "STN" | "SYP" | "SZL" | "THB" | "TJS" | "TMT" | "TND" | "TOP" | "TRY" | "TTD" | "TVD" | "TWD" | "TZS" | "UAH" | "UGX" | "USD" | "USN" | "UYI" | "UYU" | "UYW" | "UZS" | "VED" | "VES" | "VND" | "VUV" | "WST" | "XAF" | "XAG" | "XAU" | "XBA" | "XBB" | "XBC" | "XBD" | "XCD" | "XDR" | "XOF" | "XPD" | "XPF" | "XPT" | "XSU" | "XTS" | "XUA" | "XXX" | "YER" | "ZAR" | "ZMW" | "ZWL";
39
+ };
40
+ };
41
+ protected parsePaginatedResult(body: StoreOrderListResponse, query: OrderSearchQueryByTerm): {
42
+ identifier: {
43
+ term: string;
44
+ filters: string[];
45
+ paginationOptions: {
46
+ pageNumber: number;
47
+ pageSize: number;
48
+ };
49
+ partNumber?: string[] | undefined;
50
+ orderStatus?: ("AwaitingPayment" | "ReleasedToFulfillment" | "Shipped" | "Cancelled")[] | undefined;
51
+ userId?: {
52
+ userId: string;
53
+ } | undefined;
54
+ startDate?: string | undefined;
55
+ endDate?: string | undefined;
56
+ };
57
+ pageNumber: number;
58
+ pageSize: number;
59
+ totalCount: number;
60
+ totalPages: number;
61
+ items: {
62
+ identifier: {
63
+ key: string;
64
+ };
65
+ userId: {
66
+ userId: string;
67
+ };
68
+ customerName: string;
69
+ orderDate: string;
70
+ orderStatus: "AwaitingPayment" | "ReleasedToFulfillment" | "Shipped" | "Cancelled";
71
+ inventoryStatus: "NotAllocated" | "Allocated" | "Backordered" | "Preordered";
72
+ totalAmount: {
73
+ value: number;
74
+ currency: "AED" | "AFN" | "ALL" | "AMD" | "ANG" | "AOA" | "ARS" | "AUD" | "AWG" | "AZN" | "BAM" | "BBD" | "BDT" | "BGN" | "BHD" | "BIF" | "BMD" | "BND" | "BOB" | "BOV" | "BRL" | "BSD" | "BTN" | "BWP" | "BYN" | "BZD" | "CAD" | "CDF" | "CHE" | "CHF" | "CHW" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "CRC" | "CUC" | "CUP" | "CVE" | "CZK" | "DJF" | "DKK" | "DOP" | "DZD" | "EGP" | "ERN" | "ETB" | "EUR" | "FJD" | "FKP" | "GBP" | "GEL" | "GHS" | "GIP" | "GMD" | "GNF" | "GTQ" | "GYD" | "HKD" | "HNL" | "HRK" | "HTG" | "HUF" | "IDR" | "ILS" | "INR" | "IQD" | "IRR" | "ISK" | "JMD" | "JOD" | "JPY" | "KES" | "KGS" | "KHR" | "KMF" | "KPW" | "KRW" | "KWD" | "KYD" | "KZT" | "LAK" | "LBP" | "LKR" | "LRD" | "LSL" | "LYD" | "MAD" | "MDL" | "MGA" | "MKD" | "MMK" | "MNT" | "MOP" | "MRU" | "MUR" | "MVR" | "MWK" | "MXN" | "MXV" | "MYR" | "MZN" | "NAD" | "NGN" | "NIO" | "NOK" | "NPR" | "NZD" | "OMR" | "PAB" | "PEN" | "PGK" | "PHP" | "PKR" | "PLN" | "PYG" | "QAR" | "RON" | "RSD" | "RUB" | "RWF" | "SAR" | "SBD" | "SCR" | "SDG" | "SEK" | "SGD" | "SHP" | "SLE" | "SLL" | "SOS" | "SRD" | "SSP" | "STN" | "SYP" | "SZL" | "THB" | "TJS" | "TMT" | "TND" | "TOP" | "TRY" | "TTD" | "TVD" | "TWD" | "TZS" | "UAH" | "UGX" | "USD" | "USN" | "UYI" | "UYU" | "UYW" | "UZS" | "VED" | "VES" | "VND" | "VUV" | "WST" | "XAF" | "XAG" | "XAU" | "XBA" | "XBB" | "XBC" | "XBD" | "XCD" | "XDR" | "XOF" | "XPD" | "XPF" | "XPT" | "XSU" | "XTS" | "XUA" | "XXX" | "YER" | "ZAR" | "ZMW" | "ZWL";
75
+ };
76
+ shippingAddress?: {
77
+ identifier: {
78
+ nickName: string;
79
+ };
80
+ firstName: string;
81
+ lastName: string;
82
+ streetAddress: string;
83
+ streetNumber: string;
84
+ city: string;
85
+ region: string;
86
+ postalCode: string;
87
+ countryCode: string;
88
+ } | undefined;
89
+ }[];
90
+ };
91
+ }
@@ -0,0 +1,51 @@
1
+ import type { Cache, Order, OrderQueryById, RequestContext, Result, NotFoundError, CostBreakDown, Currency, OrderItem, ItemCostBreakdown } from '@reactionary/core';
2
+ import { OrderProvider } from '@reactionary/core';
3
+ import type { MedusaClient } from '../core/client.js';
4
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
5
+ import type { StoreOrder, StoreOrderLineItem, StorePaymentCollection } from '@medusajs/types';
6
+ export declare class MedusaOrderProvider extends OrderProvider {
7
+ protected config: MedusaConfiguration;
8
+ protected client: MedusaClient;
9
+ constructor(config: MedusaConfiguration, cache: Cache, context: RequestContext, client: MedusaClient);
10
+ getById(payload: OrderQueryById): Promise<Result<Order, NotFoundError>>;
11
+ /**
12
+ * Extension point to control the parsing of a single cart item price
13
+ * @param remoteItem
14
+ * @param currency
15
+ * @returns
16
+ */
17
+ protected parseItemPrice(remoteItem: StoreOrderLineItem, currency: Currency): ItemCostBreakdown;
18
+ /**
19
+ * Extension point to control the parsing of the cost breakdown of a cart
20
+ * @param remote
21
+ * @returns
22
+ */
23
+ protected parseCostBreakdown(remote: StoreOrder): CostBreakDown;
24
+ /**
25
+ * Extension point to control the parsing of a single cart item
26
+ * @param remoteItem
27
+ * @param currency
28
+ * @returns
29
+ */
30
+ protected parseOrderItem(remoteItem: StoreOrderLineItem, currency: Currency): OrderItem;
31
+ protected parsePaymentInstruction(remotePayment: StorePaymentCollection, order: StoreOrder): {
32
+ identifier: {
33
+ key: string;
34
+ };
35
+ amount: {
36
+ value: number;
37
+ currency: Currency;
38
+ };
39
+ paymentMethod: {
40
+ method: string;
41
+ name: string;
42
+ paymentProcessor: string;
43
+ };
44
+ protocolData: {
45
+ key: string;
46
+ value: string;
47
+ }[];
48
+ status: "pending" | "authorized" | "canceled" | "capture";
49
+ };
50
+ protected parseSingle(body: StoreOrder): Order;
51
+ }
@@ -0,0 +1,30 @@
1
+ import { type Profile, type ProfileMutationAddShippingAddress, type ProfileMutationMakeShippingAddressDefault, type ProfileMutationRemoveShippingAddress, type ProfileMutationSetBillingAddress, type ProfileMutationUpdate, type ProfileMutationUpdateShippingAddress, type ProfileQuerySelf as ProfileQueryById, type RequestContext, type Cache, type Result, type NotFoundError, ProfileProvider, type Address } from '@reactionary/core';
2
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
3
+ import type { MedusaClient } from '../core/client.js';
4
+ import type { StoreCreateCustomerAddress, StoreCustomer, StoreCustomerAddress } from '@medusajs/types';
5
+ /**
6
+ * Medusa Profile Provider
7
+ *
8
+ * Implements profile management using Medusa's customer APIs.
9
+ *
10
+ * TODO:
11
+ * - handle email and phone verification status properly using metadata or other means.
12
+ * - handle guest user scenarios (if applicable).
13
+ * - improve error handling and edge cases.
14
+ */
15
+ export declare class MedusaProfileProvider extends ProfileProvider {
16
+ protected config: MedusaConfiguration;
17
+ protected client: MedusaClient;
18
+ protected includedFields: string[];
19
+ constructor(config: MedusaConfiguration, cache: Cache, context: RequestContext, client: MedusaClient);
20
+ getById(payload: ProfileQueryById): Promise<Result<Profile, NotFoundError>>;
21
+ update(payload: ProfileMutationUpdate): Promise<Result<Profile, NotFoundError>>;
22
+ addShippingAddress(payload: ProfileMutationAddShippingAddress): Promise<Result<Profile, NotFoundError>>;
23
+ updateShippingAddress(payload: ProfileMutationUpdateShippingAddress): Promise<Result<Profile, NotFoundError>>;
24
+ removeShippingAddress(payload: ProfileMutationRemoveShippingAddress): Promise<Result<Profile, NotFoundError>>;
25
+ makeShippingAddressDefault(payload: ProfileMutationMakeShippingAddressDefault): Promise<Result<Profile, NotFoundError>>;
26
+ setBillingAddress(payload: ProfileMutationSetBillingAddress): Promise<Result<Profile, NotFoundError>>;
27
+ protected createMedusaAddress(address: Address): StoreCreateCustomerAddress;
28
+ protected parseAddress(address: StoreCustomerAddress): Address;
29
+ protected parseSingle(customer: StoreCustomer): Profile;
30
+ }
@@ -4,9 +4,12 @@ export declare const MedusaCapabilitiesSchema: z.ZodObject<{
4
4
  product: z.ZodOptional<z.ZodBoolean>;
5
5
  cart: z.ZodOptional<z.ZodBoolean>;
6
6
  checkout: z.ZodOptional<z.ZodBoolean>;
7
+ order: z.ZodOptional<z.ZodBoolean>;
7
8
  identity: z.ZodOptional<z.ZodBoolean>;
8
9
  inventory: z.ZodOptional<z.ZodBoolean>;
9
10
  category: z.ZodOptional<z.ZodBoolean>;
11
+ profile: z.ZodOptional<z.ZodBoolean>;
10
12
  productSearch: z.ZodOptional<z.ZodBoolean>;
13
+ orderSearch: z.ZodOptional<z.ZodBoolean>;
11
14
  }, z.core.$loose>;
12
15
  export type MedusaCapabilities = z.infer<typeof MedusaCapabilitiesSchema>;
@@ -1,9 +1,9 @@
1
- import type { StoreCart } from '@medusajs/types';
1
+ import type { StoreCart, StoreOrder } from '@medusajs/types';
2
2
  import type { CostBreakDown, Currency } from '@reactionary/core';
3
3
  /**
4
4
  * Parses cost breakdown from Medusa StoreCart
5
5
  */
6
- export declare function parseMedusaCostBreakdown(remote: StoreCart): CostBreakDown;
6
+ export declare function parseMedusaCostBreakdown(remote: StoreCart | StoreOrder): CostBreakDown;
7
7
  /**
8
8
  * Parses item price structure from Medusa line item
9
9
  */