@shopickup/adapters-mpl 0.0.1

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 (50) hide show
  1. package/README.md +43 -0
  2. package/dist/capabilities/auth.d.ts +39 -0
  3. package/dist/capabilities/auth.d.ts.map +1 -0
  4. package/dist/capabilities/auth.js +130 -0
  5. package/dist/capabilities/close.d.ts +8 -0
  6. package/dist/capabilities/close.d.ts.map +1 -0
  7. package/dist/capabilities/close.js +70 -0
  8. package/dist/capabilities/get-shipment-details.d.ts +63 -0
  9. package/dist/capabilities/get-shipment-details.d.ts.map +1 -0
  10. package/dist/capabilities/get-shipment-details.js +97 -0
  11. package/dist/capabilities/index.d.ts +10 -0
  12. package/dist/capabilities/index.d.ts.map +1 -0
  13. package/dist/capabilities/index.js +9 -0
  14. package/dist/capabilities/label.d.ts +33 -0
  15. package/dist/capabilities/label.d.ts.map +1 -0
  16. package/dist/capabilities/label.js +328 -0
  17. package/dist/capabilities/parcels.d.ts +33 -0
  18. package/dist/capabilities/parcels.d.ts.map +1 -0
  19. package/dist/capabilities/parcels.js +284 -0
  20. package/dist/capabilities/pickup-points.d.ts +41 -0
  21. package/dist/capabilities/pickup-points.d.ts.map +1 -0
  22. package/dist/capabilities/pickup-points.js +294 -0
  23. package/dist/capabilities/track.d.ts +72 -0
  24. package/dist/capabilities/track.d.ts.map +1 -0
  25. package/dist/capabilities/track.js +331 -0
  26. package/dist/index.d.ts +83 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +142 -0
  29. package/dist/mappers/label.d.ts +67 -0
  30. package/dist/mappers/label.d.ts.map +1 -0
  31. package/dist/mappers/label.js +83 -0
  32. package/dist/mappers/shipment.d.ts +110 -0
  33. package/dist/mappers/shipment.d.ts.map +1 -0
  34. package/dist/mappers/shipment.js +258 -0
  35. package/dist/mappers/tracking.d.ts +60 -0
  36. package/dist/mappers/tracking.d.ts.map +1 -0
  37. package/dist/mappers/tracking.js +187 -0
  38. package/dist/utils/httpUtils.d.ts +36 -0
  39. package/dist/utils/httpUtils.d.ts.map +1 -0
  40. package/dist/utils/httpUtils.js +76 -0
  41. package/dist/utils/oauthFallback.d.ts +47 -0
  42. package/dist/utils/oauthFallback.d.ts.map +1 -0
  43. package/dist/utils/oauthFallback.js +250 -0
  44. package/dist/utils/resolveBaseUrl.d.ts +75 -0
  45. package/dist/utils/resolveBaseUrl.d.ts.map +1 -0
  46. package/dist/utils/resolveBaseUrl.js +65 -0
  47. package/dist/validation.d.ts +1890 -0
  48. package/dist/validation.d.ts.map +1 -0
  49. package/dist/validation.js +726 -0
  50. package/package.json +69 -0
package/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # @shopickup/adapters-mpl
2
+
3
+ MPL adapter for Shopickup.
4
+
5
+ [GitHub repo](https://github.com/shopickup/shopickup-integration-layer)
6
+ [Issues](https://github.com/shopickup/shopickup-integration-layer/issues)
7
+
8
+ ## What it does
9
+
10
+ - `CREATE_PARCEL`
11
+ - `CREATE_PARCELS`
12
+ - `CREATE_LABEL`
13
+ - `CREATE_LABELS`
14
+ - `LIST_PICKUP_POINTS`
15
+ - OAuth/basic auth exchange helpers
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pnpm add @shopickup/adapters-mpl @shopickup/core
21
+ ```
22
+
23
+ ## Quick start
24
+
25
+ ```ts
26
+ import { MPLAdapter } from '@shopickup/adapters-mpl';
27
+ import { createAxiosHttpClient } from '@shopickup/core';
28
+
29
+ const adapter = new MPLAdapter();
30
+ const http = createAxiosHttpClient();
31
+
32
+ const result = await adapter.exchangeAuthToken(
33
+ {
34
+ credentials: { apiKey: 'your-api-key', apiSecret: 'your-api-secret' },
35
+ options: { useTestApi: true },
36
+ },
37
+ { http, logger: console }
38
+ );
39
+ ```
40
+
41
+ ## Status
42
+
43
+ Published as `0.x.x` while the adapter API is still evolving.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * MPL OAuth Token Exchange Capability
3
+ *
4
+ * Exchanges Basic auth credentials (apiKey + apiSecret) for an OAuth2 Bearer token.
5
+ *
6
+ * This capability allows integrators to:
7
+ * 1. Proactively exchange credentials for OAuth tokens
8
+ * 2. Cache tokens on their side (Redis, database, etc.)
9
+ * 3. Pass OAuth tokens on subsequent API calls instead of Basic auth
10
+ *
11
+ * Use cases:
12
+ * - Integrator wants to avoid Basic auth if disabled at account level
13
+ * - Integrator wants to cache tokens to reduce network calls
14
+ * - Integrator wants to manage token lifecycle explicitly
15
+ */
16
+ import type { AdapterContext } from "@shopickup/core";
17
+ import type { ResolveOAuthUrl } from "../utils/resolveBaseUrl.js";
18
+ import { type ExchangeAuthTokenRequest, type ExchangeAuthTokenResponse } from "../validation.js";
19
+ /**
20
+ * Exchange Basic auth credentials for an OAuth2 Bearer token
21
+ *
22
+ * POST to /oauth2/token with:
23
+ * - Authorization: Basic <base64(apiKey:apiSecret)>
24
+ * - Content-Type: application/x-www-form-urlencoded
25
+ * - Body: grant_type=client_credentials
26
+ * - X-Accounting-Code header (required by MPL)
27
+ * - X-Request-ID header (for tracking)
28
+ *
29
+ * Returns token with expiry information (typically 1 hour / 3600 seconds)
30
+ *
31
+ * @param req Request with apiKey + apiSecret credentials
32
+ * @param ctx Adapter context with HTTP client and logger
33
+ * @param resolveOAuthUrl Function to resolve OAuth endpoint URL (test vs. production)
34
+ * @param accountingCode Customer accounting code from MPL
35
+ * @returns ExchangeAuthTokenResponse with access_token, expires_in, etc.
36
+ * @throws CarrierError on invalid credentials or network failure
37
+ */
38
+ export declare function exchangeAuthToken(req: ExchangeAuthTokenRequest, ctx: AdapterContext, resolveOAuthUrl: ResolveOAuthUrl, accountingCode: string): Promise<ExchangeAuthTokenResponse>;
39
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/capabilities/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAG/B,MAAM,kBAAkB,CAAC;AAG1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,wBAAwB,EAC7B,GAAG,EAAE,cAAc,EACnB,eAAe,EAAE,eAAe,EAChC,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,yBAAyB,CAAC,CAuKpC"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * MPL OAuth Token Exchange Capability
3
+ *
4
+ * Exchanges Basic auth credentials (apiKey + apiSecret) for an OAuth2 Bearer token.
5
+ *
6
+ * This capability allows integrators to:
7
+ * 1. Proactively exchange credentials for OAuth tokens
8
+ * 2. Cache tokens on their side (Redis, database, etc.)
9
+ * 3. Pass OAuth tokens on subsequent API calls instead of Basic auth
10
+ *
11
+ * Use cases:
12
+ * - Integrator wants to avoid Basic auth if disabled at account level
13
+ * - Integrator wants to cache tokens to reduce network calls
14
+ * - Integrator wants to manage token lifecycle explicitly
15
+ */
16
+ import { CarrierError, safeLog, createLogEntry, serializeForLog } from "@shopickup/core";
17
+ import { safeValidateExchangeAuthTokenRequest, isGatewayError, } from "../validation.js";
18
+ import { buildMPLHeaders } from "../utils/httpUtils.js";
19
+ /**
20
+ * Exchange Basic auth credentials for an OAuth2 Bearer token
21
+ *
22
+ * POST to /oauth2/token with:
23
+ * - Authorization: Basic <base64(apiKey:apiSecret)>
24
+ * - Content-Type: application/x-www-form-urlencoded
25
+ * - Body: grant_type=client_credentials
26
+ * - X-Accounting-Code header (required by MPL)
27
+ * - X-Request-ID header (for tracking)
28
+ *
29
+ * Returns token with expiry information (typically 1 hour / 3600 seconds)
30
+ *
31
+ * @param req Request with apiKey + apiSecret credentials
32
+ * @param ctx Adapter context with HTTP client and logger
33
+ * @param resolveOAuthUrl Function to resolve OAuth endpoint URL (test vs. production)
34
+ * @param accountingCode Customer accounting code from MPL
35
+ * @returns ExchangeAuthTokenResponse with access_token, expires_in, etc.
36
+ * @throws CarrierError on invalid credentials or network failure
37
+ */
38
+ export async function exchangeAuthToken(req, ctx, resolveOAuthUrl, accountingCode) {
39
+ if (!ctx.http) {
40
+ throw new CarrierError("HTTP client not provided in adapter context", "Permanent", { raw: "Missing ctx.http" });
41
+ }
42
+ try {
43
+ // Validate request format and credentials
44
+ const validated = safeValidateExchangeAuthTokenRequest(req);
45
+ if (!validated.success) {
46
+ // Extract detailed error messages from Zod validation
47
+ const errors = validated.error.issues.map((issue) => {
48
+ const path = issue.path.length > 0 ? `${issue.path.join('.')}` : 'root';
49
+ return `${path}: ${issue.message}`;
50
+ }).join('; ');
51
+ throw new CarrierError(`Invalid request: ${errors}`, "Validation", { raw: serializeForLog(validated.error) });
52
+ }
53
+ // Only apiKey credentials can be exchanged
54
+ if (validated.data.credentials.authType !== 'apiKey') {
55
+ throw new CarrierError("exchangeAuthToken requires apiKey credentials (apiKey + apiSecret), not oauth2 token", "Validation");
56
+ }
57
+ const useTestApi = validated.data.options?.useTestApi ?? false;
58
+ const oauthUrl = resolveOAuthUrl(validated.data.options);
59
+ safeLog(ctx.logger, 'debug', 'Exchanging Basic auth credentials for OAuth token', createLogEntry({ useTestApi, endpoint: '/oauth2/token' }, null, ctx, ['exchangeAuthToken']), ctx, ['exchangeAuthToken']);
60
+ // Make the OAuth token exchange request
61
+ // Body: application/x-www-form-urlencoded with grant_type=client_credentials
62
+ const httpResponse = await ctx.http.post(oauthUrl, new URLSearchParams({
63
+ grant_type: 'client_credentials',
64
+ }).toString(), {
65
+ headers: {
66
+ ...buildMPLHeaders(validated.data.credentials, accountingCode),
67
+ 'Content-Type': 'application/x-www-form-urlencoded',
68
+ },
69
+ });
70
+ // Handle non-200 responses (auth failures)
71
+ if (httpResponse.status !== 200) {
72
+ const body = httpResponse.body;
73
+ if (isGatewayError(body)) {
74
+ const faultString = body.fault?.faultstring || 'Unknown error';
75
+ const errorCode = body.fault?.detail?.errorcode || 'UNKNOWN';
76
+ ctx.logger?.warn("OAuth token exchange failed", {
77
+ status: httpResponse.status,
78
+ errorCode,
79
+ faultString,
80
+ });
81
+ throw new CarrierError(`OAuth token exchange failed: ${faultString} (${errorCode})`, "Auth", {
82
+ carrierCode: errorCode,
83
+ raw: body,
84
+ });
85
+ }
86
+ else {
87
+ // Unexpected response format for error status
88
+ throw new CarrierError(`OAuth token exchange returned status ${httpResponse.status} with unexpected response format`, "Transient", { raw: body });
89
+ }
90
+ }
91
+ // Validate 200 response structure
92
+ const body = httpResponse.body;
93
+ if (!body || typeof body !== 'object' || !('access_token' in body)) {
94
+ throw new CarrierError("Invalid OAuth token response: missing access_token", "Permanent", { raw: body });
95
+ }
96
+ const tokenResponse = body;
97
+ if (!tokenResponse.access_token || typeof tokenResponse.access_token !== 'string') {
98
+ throw new CarrierError("Invalid OAuth token response: access_token is not a string", "Permanent", { raw: tokenResponse });
99
+ }
100
+ if (!tokenResponse.expires_in || typeof tokenResponse.expires_in !== 'number') {
101
+ throw new CarrierError("Invalid OAuth token response: expires_in is not a number", "Permanent", { raw: tokenResponse });
102
+ }
103
+ const result = {
104
+ access_token: tokenResponse.access_token,
105
+ token_type: tokenResponse.token_type || 'Bearer',
106
+ expires_in: tokenResponse.expires_in,
107
+ issued_at: tokenResponse.issued_at,
108
+ raw: tokenResponse,
109
+ };
110
+ safeLog(ctx.logger, 'info', 'OAuth token exchanged successfully', {
111
+ expiresIn: result.expires_in,
112
+ tokenType: result.token_type,
113
+ useTestApi,
114
+ }, ctx, ['exchangeAuthToken']);
115
+ return result;
116
+ }
117
+ catch (err) {
118
+ // Handle caught CarrierErrors
119
+ if (err instanceof CarrierError) {
120
+ throw err;
121
+ }
122
+ // Convert unknown errors to CarrierError
123
+ const errorMessage = err instanceof Error ? err.message : String(err);
124
+ ctx.logger?.error("Failed to exchange OAuth token", {
125
+ error: errorMessage,
126
+ type: err instanceof Error ? err.constructor.name : typeof err,
127
+ });
128
+ throw new CarrierError(`Failed to exchange OAuth token: ${errorMessage}`, "Transient", { raw: err });
129
+ }
130
+ }
@@ -0,0 +1,8 @@
1
+ import type { AdapterContext, CloseShipmentsRequest, CloseShipmentsResponse } from '@shopickup/core';
2
+ import { ResolveBaseUrl } from '../utils/resolveBaseUrl.js';
3
+ /**
4
+ * Close shipments for MPL
5
+ * Accepts core CloseShipmentsRequest-like shape but uses MPL-specific schema
6
+ */
7
+ export declare function closeShipments(req: CloseShipmentsRequest | unknown, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<CloseShipmentsResponse>;
8
+ //# sourceMappingURL=close.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../src/capabilities/close.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAIrG,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D;;;GAGG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,qBAAqB,GAAG,OAAO,EACpC,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,sBAAsB,CAAC,CAyEjC"}
@@ -0,0 +1,70 @@
1
+ import { CarrierError, serializeForLog } from '@shopickup/core';
2
+ import { safeValidateCloseShipmentsRequest } from '../validation.js';
3
+ import { buildMPLHeaders } from '../utils/httpUtils.js';
4
+ /**
5
+ * Close shipments for MPL
6
+ * Accepts core CloseShipmentsRequest-like shape but uses MPL-specific schema
7
+ */
8
+ export async function closeShipments(req, ctx, resolveBaseUrl) {
9
+ try {
10
+ // Validate full request envelope against MPL-shaped schema if possible
11
+ const validatedReq = safeValidateCloseShipmentsRequest(req);
12
+ if (!validatedReq.success) {
13
+ throw new CarrierError('Invalid close shipments request', 'Validation', { raw: validatedReq });
14
+ }
15
+ const validated = validatedReq.data;
16
+ // MPL expects trackingNumbers array + mpl accounting code under options
17
+ const body = {
18
+ fromDate: validated.close?.fromDate ?? undefined,
19
+ toDate: validated.close?.toDate ?? undefined,
20
+ trackingNumbers: validated.trackingNumbers ?? validated.close?.trackingNumbers,
21
+ checkList: validated.close?.checkList,
22
+ checkListWithPrice: validated.close?.checkListWithPrice,
23
+ tag: validated.close?.tag,
24
+ requestId: validated.close?.requestId,
25
+ summaryList: validated.close?.summaryList,
26
+ singleFile: validated.close?.singleFile,
27
+ };
28
+ if (!ctx.http) {
29
+ throw new CarrierError('HTTP client not provided in context', 'Permanent');
30
+ }
31
+ // Resolve base URL (supports useTestApi via options.useTestApi)
32
+ const baseUrl = resolveBaseUrl({ useTestApi: (validated.options?.useTestApi) ?? false });
33
+ const url = `${baseUrl}/shipments/close`;
34
+ const accountingCode = validated.options?.mpl?.accountingCode;
35
+ // Build headers (uses validated credentials + accounting code)
36
+ const headers = buildMPLHeaders(validated.credentials, accountingCode ?? '');
37
+ ctx.logger?.debug('MPL: Closing shipments', { trackingNumbers: (body.trackingNumbers || []).length });
38
+ const httpRes = await ctx.http.post(url, body, { headers });
39
+ const parsed = httpRes.body;
40
+ // If response is array of ShipmentCloseResult per OpenAPI, normalize
41
+ const results = Array.isArray(parsed) ? parsed : [parsed];
42
+ // Build core CloseShipmentsResponse
43
+ const closeResults = results.map((r) => ({
44
+ manifestId: r.dispatchId?.toString?.() ?? undefined,
45
+ manifest: r.manifest ?? r.manifestSUM ?? r.manifestRA ?? undefined,
46
+ errors: r.errors,
47
+ warnings: r.warnings,
48
+ raw: r,
49
+ }));
50
+ const successCount = closeResults.filter((c) => !c.errors || c.errors.length === 0).length;
51
+ const failureCount = closeResults.length - successCount;
52
+ return {
53
+ results: closeResults,
54
+ successCount,
55
+ failureCount,
56
+ totalCount: closeResults.length,
57
+ allSucceeded: failureCount === 0 && closeResults.length > 0,
58
+ allFailed: successCount === 0 && closeResults.length > 0,
59
+ someFailed: failureCount > 0 && successCount > 0,
60
+ summary: `${successCount} manifests generated, ${failureCount} failed`,
61
+ rawCarrierResponse: serializeForLog(httpRes),
62
+ };
63
+ }
64
+ catch (err) {
65
+ if (err instanceof CarrierError)
66
+ throw err;
67
+ ctx.logger?.error('MPL: Close shipments failed', { error: err?.message });
68
+ throw new CarrierError(`Close shipments failed: ${err?.message ?? 'Unknown'}`, 'Transient', { raw: serializeForLog(err) });
69
+ }
70
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * MPL Adapter: Get Shipment Details Capability
3
+ * Handles GET_SHIPMENT_DETAILS operation via GET /shipments/{trackingNumber}
4
+ *
5
+ * Retrieves shipment metadata including sender, recipient, items, and shipment state.
6
+ * Note: This returns shipment details/metadata, not tracking event history.
7
+ */
8
+ import type { AdapterContext } from '@shopickup/core';
9
+ import type { ResolveBaseUrl } from '../utils/resolveBaseUrl.js';
10
+ /**
11
+ * Response type for GET_SHIPMENT_DETAILS operation
12
+ * Contains normalized shipment metadata from MPL API
13
+ */
14
+ export interface ShipmentDetailsResponse {
15
+ /** Tracking/shipment number */
16
+ trackingNumber?: string;
17
+ /** Order ID if applicable */
18
+ orderId?: string;
19
+ /** Shipment date if available */
20
+ shipmentDate?: string;
21
+ /** Sender details */
22
+ sender?: {
23
+ name?: string;
24
+ street?: string;
25
+ city?: string;
26
+ postalCode?: string;
27
+ country?: string;
28
+ phone?: string;
29
+ };
30
+ /** Recipient details */
31
+ recipient?: {
32
+ name?: string;
33
+ street?: string;
34
+ city?: string;
35
+ postalCode?: string;
36
+ country?: string;
37
+ phone?: string;
38
+ };
39
+ /** Items in shipment */
40
+ items?: Array<{
41
+ id?: string;
42
+ weight?: number;
43
+ [key: string]: any;
44
+ }>;
45
+ /** Full raw response from MPL API */
46
+ raw: any;
47
+ }
48
+ /**
49
+ * Get shipment details by tracking number via GET /shipments/{trackingNumber}
50
+ *
51
+ * Returns shipment metadata (sender, recipient, items, dates).
52
+ * This is NOT a tracking operation - it retrieves shipment state and details.
53
+ *
54
+ * To use test API, pass in request with options.useTestApi = true
55
+ */
56
+ export declare function getShipmentDetails(req: {
57
+ trackingNumber: string;
58
+ credentials?: any;
59
+ options?: {
60
+ useTestApi?: boolean;
61
+ };
62
+ }, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<ShipmentDetailsResponse>;
63
+ //# sourceMappingURL=get-shipment-details.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-shipment-details.d.ts","sourceRoot":"","sources":["../../src/capabilities/get-shipment-details.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,+BAA+B;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,wBAAwB;IACxB,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,wBAAwB;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC,CAAC;IACH,qCAAqC;IACrC,GAAG,EAAE,GAAG,CAAC;CACV;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CACpC,EACD,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,uBAAuB,CAAC,CAmHlC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * MPL Adapter: Get Shipment Details Capability
3
+ * Handles GET_SHIPMENT_DETAILS operation via GET /shipments/{trackingNumber}
4
+ *
5
+ * Retrieves shipment metadata including sender, recipient, items, and shipment state.
6
+ * Note: This returns shipment details/metadata, not tracking event history.
7
+ */
8
+ import { CarrierError, serializeForLog, errorToLog } from '@shopickup/core';
9
+ import { safeValidateGetShipmentDetailsRequest, safeValidateShipmentQueryResponse } from '../validation.js';
10
+ import { buildMPLHeaders } from '../utils/httpUtils.js';
11
+ /**
12
+ * Get shipment details by tracking number via GET /shipments/{trackingNumber}
13
+ *
14
+ * Returns shipment metadata (sender, recipient, items, dates).
15
+ * This is NOT a tracking operation - it retrieves shipment state and details.
16
+ *
17
+ * To use test API, pass in request with options.useTestApi = true
18
+ */
19
+ export async function getShipmentDetails(req, ctx, resolveBaseUrl) {
20
+ try {
21
+ // Validate request format and credentials
22
+ const validated = safeValidateGetShipmentDetailsRequest(req);
23
+ if (!validated.success) {
24
+ throw new CarrierError(`Invalid request: ${validated.error.message}`, 'Validation', { raw: serializeForLog(validated.error) });
25
+ }
26
+ if (!ctx.http) {
27
+ throw new CarrierError('HTTP client not provided in context', 'Permanent');
28
+ }
29
+ // Extract accountingCode from credentials (required for MPL)
30
+ const accountingCode = validated.data.credentials?.accountingCode;
31
+ if (!accountingCode) {
32
+ throw new CarrierError('accountingCode is required in credentials', 'Validation');
33
+ }
34
+ // Extract useTestApi from validated request
35
+ const useTestApi = validated.data.options?.useTestApi ?? false;
36
+ const baseUrl = resolveBaseUrl(validated.data.options);
37
+ const trackingNumber = validated.data.trackingNumber;
38
+ ctx.logger?.debug('MPL: Getting shipment details', {
39
+ trackingNumber,
40
+ testMode: useTestApi,
41
+ });
42
+ // Get shipment details via GET /shipments/{trackingNumber} endpoint
43
+ const url = `${baseUrl}/shipments/${encodeURIComponent(trackingNumber)}`;
44
+ const httpResponse = await ctx.http.get(url, {
45
+ headers: buildMPLHeaders(validated.data.credentials, accountingCode),
46
+ });
47
+ // Extract body from normalized HttpResponse
48
+ const response = httpResponse.body;
49
+ // Validate response against Zod schema
50
+ const responseValidation = safeValidateShipmentQueryResponse(response);
51
+ if (!responseValidation.success) {
52
+ throw new CarrierError(`Invalid shipment response: ${responseValidation.error.message}`, 'Validation', { raw: serializeForLog(responseValidation.error) });
53
+ }
54
+ const validatedResponse = responseValidation.data;
55
+ // Check for errors in response
56
+ if (validatedResponse.errors && validatedResponse.errors.length > 0) {
57
+ const errorMsg = validatedResponse.errors
58
+ .map((e) => e.text || e.code)
59
+ .join('; ');
60
+ const notFound = validatedResponse.errors.some((e) => {
61
+ const code = String(e.code || '').toUpperCase();
62
+ const text = String(e.text || '').toLowerCase();
63
+ return code.includes('NOT_FOUND') || text.includes('not found');
64
+ });
65
+ throw new CarrierError(`MPL error: ${errorMsg}`, notFound ? 'NotFound' : 'Validation', { raw: validatedResponse.errors });
66
+ }
67
+ if (!validatedResponse.shipment) {
68
+ throw new CarrierError(`No shipment found for tracking number ${trackingNumber}`, 'NotFound');
69
+ }
70
+ const shipment = validatedResponse.shipment;
71
+ ctx.logger?.info('MPL: Shipment details retrieved', {
72
+ trackingNumber,
73
+ itemCount: shipment.items?.length || 0,
74
+ testMode: useTestApi,
75
+ });
76
+ // Return shipment details normalized to response type
77
+ return {
78
+ trackingNumber: shipment.trackingNumber,
79
+ orderId: shipment.orderId,
80
+ shipmentDate: shipment.shipmentDate,
81
+ sender: shipment.sender,
82
+ recipient: shipment.recipient,
83
+ items: shipment.items,
84
+ raw: validatedResponse,
85
+ };
86
+ }
87
+ catch (error) {
88
+ if (error instanceof CarrierError) {
89
+ throw error;
90
+ }
91
+ ctx.logger?.error('MPL: Error getting shipment details', {
92
+ trackingNumber: req.trackingNumber,
93
+ error: errorToLog(error),
94
+ });
95
+ throw new CarrierError(`Failed to get shipment details: ${error?.message || "Unknown error"}`, "Transient", { raw: serializeForLog(error) });
96
+ }
97
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Foxpost Adapter: Capabilities Export
3
+ * Re-exports all capability methods for clean imports
4
+ */
5
+ export { createParcel, createParcels } from './parcels.js';
6
+ export { createLabel, createLabels } from './label.js';
7
+ export { fetchPickupPoints } from './pickup-points.js';
8
+ export { exchangeAuthToken } from './auth.js';
9
+ export { closeShipments } from './close.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/capabilities/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Foxpost Adapter: Capabilities Export
3
+ * Re-exports all capability methods for clean imports
4
+ */
5
+ export { createParcel, createParcels } from './parcels.js';
6
+ export { createLabel, createLabels } from './label.js';
7
+ export { fetchPickupPoints } from './pickup-points.js';
8
+ export { exchangeAuthToken } from './auth.js';
9
+ export { closeShipments } from './close.js';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * MPL Adapter: Label Generation Capability
3
+ * Handles CREATE_LABEL and CREATE_LABELS operations
4
+ *
5
+ * Key differences from Foxpost:
6
+ * - Uses GET request instead of POST
7
+ * - Returns JSON array with base64-encoded label data
8
+ * - Multiple query parameters including labelType, labelFormat, orderBy, singleFile
9
+ * - Per-item error handling in the response array
10
+ */
11
+ import type { AdapterContext, CreateLabelResponse, CreateLabelsResponse } from "@shopickup/core";
12
+ import type { CreateLabelMPLRequest, CreateLabelsMPLRequest } from '../validation.js';
13
+ import type { ResolveBaseUrl } from '../utils/resolveBaseUrl.js';
14
+ /**
15
+ * Create a label (generate PDF) for a single parcel
16
+ * Delegates to createLabels to reuse batching logic
17
+ *
18
+ * Returns Promise<LabelResult> with file mapping and metadata
19
+ */
20
+ export declare function createLabel(req: CreateLabelMPLRequest, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<CreateLabelResponse>;
21
+ /**
22
+ * Create labels for multiple parcels in one call
23
+ *
24
+ * MPL GET /shipments/label endpoint:
25
+ * - Takes array of tracking numbers (query params)
26
+ * - Optional labelType, labelFormat, orderBy, singleFile params
27
+ * - Returns JSON array of LabelQueryResult objects with base64-encoded label data
28
+ * - Each result has trackingNumber, label (base64), errors/warnings arrays
29
+ *
30
+ * Returns structured response with files array and per-item results
31
+ */
32
+ export declare function createLabels(req: CreateLabelsMPLRequest, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<CreateLabelsResponse>;
33
+ //# sourceMappingURL=label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../src/capabilities/label.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAEV,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAkB,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAoEjE;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,qBAAqB,EAC1B,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CAiD9B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,sBAAsB,EAC3B,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAmP/B"}