@shopickup/adapters-foxpost 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.
- package/README.md +48 -0
- package/dist/capabilities/index.d.ts +9 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +8 -0
- package/dist/capabilities/label.d.ts +27 -0
- package/dist/capabilities/label.d.ts.map +1 -0
- package/dist/capabilities/label.js +370 -0
- package/dist/capabilities/parcels.d.ts +21 -0
- package/dist/capabilities/parcels.d.ts.map +1 -0
- package/dist/capabilities/parcels.js +233 -0
- package/dist/capabilities/pickup-points.d.ts +38 -0
- package/dist/capabilities/pickup-points.d.ts.map +1 -0
- package/dist/capabilities/pickup-points.js +225 -0
- package/dist/capabilities/track.d.ts +16 -0
- package/dist/capabilities/track.d.ts.map +1 -0
- package/dist/capabilities/track.js +99 -0
- package/dist/client/index.d.ts +17 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +30 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +165 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +151 -0
- package/dist/mappers/index.d.ts +108 -0
- package/dist/mappers/index.d.ts.map +1 -0
- package/dist/mappers/index.js +270 -0
- package/dist/mappers/trackStatus.d.ts +58 -0
- package/dist/mappers/trackStatus.d.ts.map +1 -0
- package/dist/mappers/trackStatus.js +290 -0
- package/dist/types/generated.d.ts +177 -0
- package/dist/types/generated.d.ts.map +1 -0
- package/dist/types/generated.js +9 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/utils/httpUtils.d.ts +18 -0
- package/dist/utils/httpUtils.d.ts.map +1 -0
- package/dist/utils/httpUtils.js +33 -0
- package/dist/utils/resolveBaseUrl.d.ts +23 -0
- package/dist/utils/resolveBaseUrl.d.ts.map +1 -0
- package/dist/utils/resolveBaseUrl.js +19 -0
- package/dist/validation.d.ts +1723 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +799 -0
- package/package.json +68 -0
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CarrierError } from "@shopickup/core";
|
|
2
|
+
/**
|
|
3
|
+
* Translate Foxpost errors to structured CarrierError
|
|
4
|
+
*
|
|
5
|
+
* Supports errors from any HTTP client implementation:
|
|
6
|
+
* - axios
|
|
7
|
+
* - node-fetch
|
|
8
|
+
* - undici
|
|
9
|
+
* - custom HTTP clients
|
|
10
|
+
* - network errors
|
|
11
|
+
*
|
|
12
|
+
* Error categorization:
|
|
13
|
+
* - 400: Validation (client error, don't retry)
|
|
14
|
+
* - 401/403: Auth (authentication failure, check credentials)
|
|
15
|
+
* - 429: RateLimit (rate limited, retry with backoff)
|
|
16
|
+
* - 5xx: Transient (server error, retry)
|
|
17
|
+
* - Network: Transient (connection issue, retry)
|
|
18
|
+
*/
|
|
19
|
+
export declare function translateFoxpostError(error: unknown): CarrierError;
|
|
20
|
+
/**
|
|
21
|
+
* Sanitize an HTTP response object for safe logging
|
|
22
|
+
* Removes sensitive headers (Authorization, Api-key, etc.) before serialization
|
|
23
|
+
*
|
|
24
|
+
* Safely handles various HTTP client response shapes:
|
|
25
|
+
* - axios: { data, status, headers, config }
|
|
26
|
+
* - fetch: { status, headers, body }
|
|
27
|
+
* - undici: { status, headers, body }
|
|
28
|
+
* - custom: any object with headers property
|
|
29
|
+
*
|
|
30
|
+
* @param response HTTP response object (may be from any HTTP client)
|
|
31
|
+
* @returns Sanitized copy safe for logging (original not modified)
|
|
32
|
+
*/
|
|
33
|
+
export declare function sanitizeResponseForLog(response: unknown): unknown;
|
|
34
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA0C,MAAM,iBAAiB,CAAC;AAiFvF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAiElE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAqCjE"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { CarrierError, sanitizeHeadersForLog } from "@shopickup/core";
|
|
2
|
+
/**
|
|
3
|
+
* Foxpost-specific error code translations
|
|
4
|
+
*/
|
|
5
|
+
const FoxpostErrorCodes = {
|
|
6
|
+
WRONG_USERNAME_OR_PASSWORD: {
|
|
7
|
+
category: "Auth",
|
|
8
|
+
message: "Invalid Foxpost credentials",
|
|
9
|
+
},
|
|
10
|
+
INVALID_APM_ID: {
|
|
11
|
+
category: "Validation",
|
|
12
|
+
message: "Invalid APM (locker) ID",
|
|
13
|
+
},
|
|
14
|
+
INVALID_RECIPIENT: {
|
|
15
|
+
category: "Validation",
|
|
16
|
+
message: "Invalid recipient information",
|
|
17
|
+
},
|
|
18
|
+
INVALID_ADDRESS: {
|
|
19
|
+
category: "Validation",
|
|
20
|
+
message: "Invalid address provided",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Extract HTTP status code from error object
|
|
25
|
+
* Works with errors from different HTTP clients (axios, fetch, undici, custom)
|
|
26
|
+
*
|
|
27
|
+
* Supports common error shapes:
|
|
28
|
+
* - axios: error.response.status
|
|
29
|
+
* - fetch: error.status (when wrapped)
|
|
30
|
+
* - undici: error.statusCode
|
|
31
|
+
* - generic: error.response?.status or error.status
|
|
32
|
+
*/
|
|
33
|
+
function extractHttpStatus(error) {
|
|
34
|
+
const anyErr = error;
|
|
35
|
+
// Try multiple common locations for HTTP status
|
|
36
|
+
return (anyErr?.response?.status ?? // axios, fetch-like wrappers
|
|
37
|
+
anyErr?.status ?? // direct status property
|
|
38
|
+
anyErr?.statusCode ?? // undici, some node wrappers
|
|
39
|
+
anyErr?.code === 'ECONNREFUSED' ? undefined : undefined // network error, not HTTP status
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extract response body from error object
|
|
44
|
+
* Works with errors from different HTTP clients
|
|
45
|
+
*
|
|
46
|
+
* Supports common error shapes:
|
|
47
|
+
* - axios: error.response.data
|
|
48
|
+
* - fetch: error.response?.json() or error.data
|
|
49
|
+
* - undici: error.body or error.data
|
|
50
|
+
*/
|
|
51
|
+
function extractResponseBody(error) {
|
|
52
|
+
const anyErr = error;
|
|
53
|
+
return (anyErr?.response?.data ?? // axios, generic response objects
|
|
54
|
+
anyErr?.data ?? // direct data property (undici, fetch wrappers)
|
|
55
|
+
anyErr?.body ?? // node-fetch, undici
|
|
56
|
+
anyErr?.response // fallback to whole response
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract error code from response body
|
|
61
|
+
* Looks for common field names across different APIs
|
|
62
|
+
*/
|
|
63
|
+
function extractErrorCode(responseBody) {
|
|
64
|
+
const body = responseBody;
|
|
65
|
+
return (body?.error ?? // common JSON API error field
|
|
66
|
+
body?.code ?? // error code field
|
|
67
|
+
body?.errorCode ?? // camelCase variant
|
|
68
|
+
body?.error_code // snake_case variant
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Translate Foxpost errors to structured CarrierError
|
|
73
|
+
*
|
|
74
|
+
* Supports errors from any HTTP client implementation:
|
|
75
|
+
* - axios
|
|
76
|
+
* - node-fetch
|
|
77
|
+
* - undici
|
|
78
|
+
* - custom HTTP clients
|
|
79
|
+
* - network errors
|
|
80
|
+
*
|
|
81
|
+
* Error categorization:
|
|
82
|
+
* - 400: Validation (client error, don't retry)
|
|
83
|
+
* - 401/403: Auth (authentication failure, check credentials)
|
|
84
|
+
* - 429: RateLimit (rate limited, retry with backoff)
|
|
85
|
+
* - 5xx: Transient (server error, retry)
|
|
86
|
+
* - Network: Transient (connection issue, retry)
|
|
87
|
+
*/
|
|
88
|
+
export function translateFoxpostError(error) {
|
|
89
|
+
const anyErr = error;
|
|
90
|
+
// Extract common error properties in a client-agnostic way
|
|
91
|
+
const status = extractHttpStatus(error);
|
|
92
|
+
const responseBody = extractResponseBody(error);
|
|
93
|
+
const errorCode = extractErrorCode(responseBody) || (typeof status === 'number' ? `HTTP_${status}` : undefined);
|
|
94
|
+
const errorMapping = errorCode ? FoxpostErrorCodes[errorCode] : undefined;
|
|
95
|
+
// Construct metadata with all available context
|
|
96
|
+
const meta = {
|
|
97
|
+
carrierCode: errorCode,
|
|
98
|
+
raw: responseBody ?? anyErr,
|
|
99
|
+
};
|
|
100
|
+
// Route by HTTP status code
|
|
101
|
+
if (typeof status === 'number') {
|
|
102
|
+
if (status === 400) {
|
|
103
|
+
return new CarrierError(`Validation error: ${errorMapping?.message || responseBody?.error || "Bad request"}`, "Validation", meta);
|
|
104
|
+
}
|
|
105
|
+
if (status === 401 || status === 403) {
|
|
106
|
+
return new CarrierError("Foxpost credentials invalid", "Auth", meta);
|
|
107
|
+
}
|
|
108
|
+
if (status === 429) {
|
|
109
|
+
return new CarrierError("Foxpost rate limit exceeded", "RateLimit", { ...meta, retryAfterMs: 60000 });
|
|
110
|
+
}
|
|
111
|
+
if (status >= 500) {
|
|
112
|
+
return new CarrierError("Foxpost server error", "Transient", meta);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Network error (Error instance with no HTTP status)
|
|
116
|
+
if (error instanceof Error) {
|
|
117
|
+
return new CarrierError(`Foxpost connection error: ${error.message}`, "Transient", { raw: anyErr });
|
|
118
|
+
}
|
|
119
|
+
// Unknown error shape
|
|
120
|
+
return new CarrierError("Unknown Foxpost error", "Permanent", { raw: anyErr });
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Sanitize an HTTP response object for safe logging
|
|
124
|
+
* Removes sensitive headers (Authorization, Api-key, etc.) before serialization
|
|
125
|
+
*
|
|
126
|
+
* Safely handles various HTTP client response shapes:
|
|
127
|
+
* - axios: { data, status, headers, config }
|
|
128
|
+
* - fetch: { status, headers, body }
|
|
129
|
+
* - undici: { status, headers, body }
|
|
130
|
+
* - custom: any object with headers property
|
|
131
|
+
*
|
|
132
|
+
* @param response HTTP response object (may be from any HTTP client)
|
|
133
|
+
* @returns Sanitized copy safe for logging (original not modified)
|
|
134
|
+
*/
|
|
135
|
+
export function sanitizeResponseForLog(response) {
|
|
136
|
+
if (!response || typeof response !== 'object') {
|
|
137
|
+
return response;
|
|
138
|
+
}
|
|
139
|
+
const resp = response;
|
|
140
|
+
// Create a shallow copy to avoid modifying original
|
|
141
|
+
const sanitized = {};
|
|
142
|
+
for (const [key, value] of Object.entries(resp)) {
|
|
143
|
+
// Always skip request body (should not be in response, but be safe)
|
|
144
|
+
if (key === 'body' && typeof value === 'string' && value.length > 10000) {
|
|
145
|
+
sanitized[key] = '[Large binary or text body - truncated for logging]';
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
// Sanitize headers property if present
|
|
149
|
+
if (key === 'headers' && typeof value === 'object') {
|
|
150
|
+
sanitized[key] = sanitizeHeadersForLog(value);
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
// Sanitize config.headers (axios pattern)
|
|
154
|
+
if (key === 'config' && typeof value === 'object') {
|
|
155
|
+
sanitized[key] = {
|
|
156
|
+
...value,
|
|
157
|
+
headers: sanitizeHeadersForLog(value?.headers),
|
|
158
|
+
};
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// Include all other properties as-is
|
|
162
|
+
sanitized[key] = value;
|
|
163
|
+
}
|
|
164
|
+
return sanitized;
|
|
165
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foxpost Carrier Adapter
|
|
3
|
+
* Implements the CarrierAdapter interface for Foxpost logistics
|
|
4
|
+
*/
|
|
5
|
+
import type { CarrierAdapter, Capability, AdapterContext, CreateParcelRequest, CreateParcelsRequest, TrackingRequest, RatesRequest, CreateParcelsResponse, CreateLabelRequest, CreateLabelResponse, CreateLabelsRequest, CreateLabelsResponse, CarrierResource, TrackingUpdate, FetchPickupPointsRequest, FetchPickupPointsResponse } from "@shopickup/core";
|
|
6
|
+
/**
|
|
7
|
+
* FoxpostAdapter
|
|
8
|
+
*
|
|
9
|
+
* Foxpost (hu-foxpost) is a major Hungarian logistics carrier.
|
|
10
|
+
*
|
|
11
|
+
* Capabilities supported:
|
|
12
|
+
* - CREATE_PARCEL: Create parcels directly
|
|
13
|
+
* - CREATE_PARCELS: Batch create multiple parcels
|
|
14
|
+
* - CREATE_LABEL: Generate PDF labels for parcels
|
|
15
|
+
* - TRACK: Track parcels by barcode
|
|
16
|
+
* - TEST_MODE_SUPPORTED: Can switch to test API for sandbox testing
|
|
17
|
+
*
|
|
18
|
+
* Test API:
|
|
19
|
+
* - Production: https://webapi.foxpost.hu
|
|
20
|
+
* - Test/Sandbox: https://webapi-test.foxpost.hu
|
|
21
|
+
* - Pass options.useTestApi = true in request to switch to test endpoint for that call
|
|
22
|
+
* - Test API requires separate test credentials
|
|
23
|
+
*
|
|
24
|
+
* Notes:
|
|
25
|
+
* - Foxpost does NOT have a shipment concept; parcels are created directly
|
|
26
|
+
* - Labels are generated per parcel
|
|
27
|
+
* - Tracking available via barcode (FoxWeb barcode format: CLFOX...)
|
|
28
|
+
* - createLabel does not support per-call test mode (no request object in interface)
|
|
29
|
+
*/
|
|
30
|
+
export declare class FoxpostAdapter implements CarrierAdapter {
|
|
31
|
+
readonly id = "hu-foxpost";
|
|
32
|
+
readonly displayName = "Foxpost Hungary";
|
|
33
|
+
readonly capabilities: Capability[];
|
|
34
|
+
readonly requires: {
|
|
35
|
+
createLabel: "CREATE_PARCEL"[];
|
|
36
|
+
};
|
|
37
|
+
private prodBaseUrl;
|
|
38
|
+
private testBaseUrl;
|
|
39
|
+
private resolveBaseUrl;
|
|
40
|
+
constructor(baseUrl?: string);
|
|
41
|
+
/**
|
|
42
|
+
* Create a parcel in Foxpost
|
|
43
|
+
*
|
|
44
|
+
* Note: Shipper information is not sent to Foxpost API.
|
|
45
|
+
* Foxpost derives the shipper from the API key's account settings.
|
|
46
|
+
* We require shipper in the core Parcel type for consistency across adapters.
|
|
47
|
+
*
|
|
48
|
+
* Maps canonical Parcel to Foxpost CreateParcelRequest (and carrier-specific type)
|
|
49
|
+
* Returns the parcel barcode as carrierId
|
|
50
|
+
*/
|
|
51
|
+
createParcel(req: CreateParcelRequest, ctx: AdapterContext): Promise<CarrierResource>;
|
|
52
|
+
/**
|
|
53
|
+
* Create multiple parcels in one call
|
|
54
|
+
* Maps canonical Parcel array to Foxpost CreateParcelRequest and calls the
|
|
55
|
+
* Foxpost batch endpoint which accepts an array. Returns per-item CarrierResource
|
|
56
|
+
* so callers can handle partial failures.
|
|
57
|
+
*
|
|
58
|
+
* Validates both the incoming parcels and the mapped carrier-specific payloads.
|
|
59
|
+
*
|
|
60
|
+
* @returns CreateParcelsResponse with summary and per-item results
|
|
61
|
+
*/
|
|
62
|
+
createParcels(req: CreateParcelsRequest, ctx: AdapterContext): Promise<CreateParcelsResponse>;
|
|
63
|
+
/**
|
|
64
|
+
* Create a label (generate PDF) for a parcel
|
|
65
|
+
*
|
|
66
|
+
* @param req CreateLabelRequest with parcelCarrierId (Foxpost barcode)
|
|
67
|
+
* @param ctx AdapterContext with HTTP client and logger
|
|
68
|
+
* @returns LabelResult with file mapping and page range
|
|
69
|
+
*/
|
|
70
|
+
createLabel(req: CreateLabelRequest, ctx: AdapterContext): Promise<CreateLabelResponse>;
|
|
71
|
+
/**
|
|
72
|
+
* Create labels for multiple parcels in one batch call
|
|
73
|
+
*
|
|
74
|
+
* Generates a single PDF with all requested labels using Foxpost POST /api/label/{pageSize}
|
|
75
|
+
* Returns per-item results for tracking success/failure of each label
|
|
76
|
+
*
|
|
77
|
+
* @param req CreateLabelsRequest with array of parcelCarrierIds
|
|
78
|
+
* @param ctx AdapterContext with HTTP client and logger
|
|
79
|
+
* @returns CreateLabelsResponse with per-item results and summary
|
|
80
|
+
*/
|
|
81
|
+
createLabels(req: CreateLabelsRequest, ctx: AdapterContext): Promise<CreateLabelsResponse>;
|
|
82
|
+
/**
|
|
83
|
+
* NOT IMPLEMENTED: Foxpost doesn't support voiding labels
|
|
84
|
+
*/
|
|
85
|
+
voidLabel(_labelId: string, _ctx: AdapterContext): Promise<CarrierResource>;
|
|
86
|
+
/**
|
|
87
|
+
* Track a parcel by its clFoxId or uniqueBarcode using the new GET /api/tracking/{barcode} endpoint
|
|
88
|
+
*
|
|
89
|
+
* Returns normalized tracking information with all available traces in reverse chronological order
|
|
90
|
+
*
|
|
91
|
+
* To use test API, pass in request as:
|
|
92
|
+
* { trackingNumber: barcode, credentials: {...}, options?: { useTestApi: true } }
|
|
93
|
+
*/
|
|
94
|
+
track(req: TrackingRequest, ctx: AdapterContext): Promise<TrackingUpdate>;
|
|
95
|
+
/**
|
|
96
|
+
* Fetch list of Foxpost pickup points (APMs)
|
|
97
|
+
*
|
|
98
|
+
* Fetches the public JSON feed from https://cdn.foxpost.hu/foxplus.json
|
|
99
|
+
* which is updated hourly and contains all active APM locations.
|
|
100
|
+
*
|
|
101
|
+
* No authentication is required as this is a public feed.
|
|
102
|
+
*
|
|
103
|
+
* @param req FetchPickupPointsRequest (optional filters)
|
|
104
|
+
* @param ctx AdapterContext with HTTP client
|
|
105
|
+
* @returns FetchPickupPointsResponse with normalized pickup points
|
|
106
|
+
*/
|
|
107
|
+
fetchPickupPoints(req: FetchPickupPointsRequest, ctx: AdapterContext): Promise<FetchPickupPointsResponse>;
|
|
108
|
+
/**
|
|
109
|
+
* NOT IMPLEMENTED: Foxpost doesn't support pickup requests
|
|
110
|
+
*/
|
|
111
|
+
requestPickup(_req: any, _ctx: AdapterContext): Promise<CarrierResource>;
|
|
112
|
+
/**
|
|
113
|
+
* NOT IMPLEMENTED: Foxpost doesn't expose rate quotes
|
|
114
|
+
*/
|
|
115
|
+
getRates(_req: RatesRequest, _ctx: AdapterContext): Promise<any>;
|
|
116
|
+
}
|
|
117
|
+
export default FoxpostAdapter;
|
|
118
|
+
export { safeValidateTrackingRequest, safeValidateFoxpostTracking, validateFoxpostTracking, safeValidateFoxpostCredentials, validateFoxpostCredentials, type FoxpostTracking, type FoxpostTrace, type FoxpostTrackDTO, } from './validation.js';
|
|
119
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AAgBzB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,cAAe,YAAW,cAAc;IACnD,QAAQ,CAAC,EAAE,gBAAgB;IAC3B,QAAQ,CAAC,WAAW,qBAAqB;IAEzC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,CAOjC;IAGF,QAAQ,CAAC,QAAQ;;MAEf;IAEF,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,WAAW,CAAoC;IACvD,OAAO,CAAC,cAAc,CAAiB;gBAE3B,OAAO,GAAE,MAAoC;IAMzD;;;;;;;;;OASG;IACG,YAAY,CAChB,GAAG,EAAE,mBAAmB,EACxB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,eAAe,CAAC;IAM3B;;;;;;;;;OASG;IACG,aAAa,CACjB,GAAG,EAAE,oBAAoB,EACzB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,qBAAqB,CAAC;IAIjC;;;;;;OAMG;IACG,WAAW,CACf,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,mBAAmB,CAAC;IAI/B;;;;;;;;;OASG;IACG,YAAY,CAChB,GAAG,EAAE,mBAAmB,EACxB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,oBAAoB,CAAC;IAIhC;;OAEG;IACG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,eAAe,CAAC;IAI3B;;;;;;;OAOG;IACG,KAAK,CACT,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,cAAc,CAAC;IAI1B;;;;;;;;;;;OAWG;IACG,iBAAiB,CACrB,GAAG,EAAE,wBAAwB,EAC7B,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,yBAAyB,CAAC;IAIrC;;OAEG;IACG,aAAa,CACjB,IAAI,EAAE,GAAG,EACT,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,eAAe,CAAC;IAI3B;;OAEG;IACG,QAAQ,CACZ,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,GAAG,CAAC;CAGhB;AAED,eAAe,cAAc,CAAC;AAG9B,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,uBAAuB,EACvB,8BAA8B,EAC9B,0BAA0B,EAC1B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,eAAe,GACrB,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Foxpost Carrier Adapter
|
|
3
|
+
* Implements the CarrierAdapter interface for Foxpost logistics
|
|
4
|
+
*/
|
|
5
|
+
import { Capabilities, NotImplementedError } from "@shopickup/core";
|
|
6
|
+
import { createParcel as createParcelImpl, createParcels as createParcelsImpl, createLabel as createLabelImpl, createLabels as createLabelsImpl, track as trackImpl, fetchPickupPoints as fetchPickupPointsImpl, } from './capabilities/index.js';
|
|
7
|
+
import { createResolveBaseUrl } from './utils/resolveBaseUrl.js';
|
|
8
|
+
/**
|
|
9
|
+
* FoxpostAdapter
|
|
10
|
+
*
|
|
11
|
+
* Foxpost (hu-foxpost) is a major Hungarian logistics carrier.
|
|
12
|
+
*
|
|
13
|
+
* Capabilities supported:
|
|
14
|
+
* - CREATE_PARCEL: Create parcels directly
|
|
15
|
+
* - CREATE_PARCELS: Batch create multiple parcels
|
|
16
|
+
* - CREATE_LABEL: Generate PDF labels for parcels
|
|
17
|
+
* - TRACK: Track parcels by barcode
|
|
18
|
+
* - TEST_MODE_SUPPORTED: Can switch to test API for sandbox testing
|
|
19
|
+
*
|
|
20
|
+
* Test API:
|
|
21
|
+
* - Production: https://webapi.foxpost.hu
|
|
22
|
+
* - Test/Sandbox: https://webapi-test.foxpost.hu
|
|
23
|
+
* - Pass options.useTestApi = true in request to switch to test endpoint for that call
|
|
24
|
+
* - Test API requires separate test credentials
|
|
25
|
+
*
|
|
26
|
+
* Notes:
|
|
27
|
+
* - Foxpost does NOT have a shipment concept; parcels are created directly
|
|
28
|
+
* - Labels are generated per parcel
|
|
29
|
+
* - Tracking available via barcode (FoxWeb barcode format: CLFOX...)
|
|
30
|
+
* - createLabel does not support per-call test mode (no request object in interface)
|
|
31
|
+
*/
|
|
32
|
+
export class FoxpostAdapter {
|
|
33
|
+
id = "hu-foxpost";
|
|
34
|
+
displayName = "Foxpost Hungary";
|
|
35
|
+
capabilities = [
|
|
36
|
+
Capabilities.CREATE_PARCEL,
|
|
37
|
+
Capabilities.CREATE_PARCELS,
|
|
38
|
+
Capabilities.CREATE_LABEL,
|
|
39
|
+
Capabilities.TRACK,
|
|
40
|
+
Capabilities.LIST_PICKUP_POINTS,
|
|
41
|
+
Capabilities.TEST_MODE_SUPPORTED,
|
|
42
|
+
];
|
|
43
|
+
// Foxpost doesn't require close before label
|
|
44
|
+
requires = {
|
|
45
|
+
createLabel: [Capabilities.CREATE_PARCEL],
|
|
46
|
+
};
|
|
47
|
+
prodBaseUrl = "https://webapi.foxpost.hu";
|
|
48
|
+
testBaseUrl = "https://webapi-test.foxpost.hu";
|
|
49
|
+
resolveBaseUrl;
|
|
50
|
+
constructor(baseUrl = "https://webapi.foxpost.hu") {
|
|
51
|
+
this.prodBaseUrl = "https://webapi.foxpost.hu";
|
|
52
|
+
this.testBaseUrl = "https://webapi-test.foxpost.hu";
|
|
53
|
+
this.resolveBaseUrl = createResolveBaseUrl(this.prodBaseUrl, this.testBaseUrl);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a parcel in Foxpost
|
|
57
|
+
*
|
|
58
|
+
* Note: Shipper information is not sent to Foxpost API.
|
|
59
|
+
* Foxpost derives the shipper from the API key's account settings.
|
|
60
|
+
* We require shipper in the core Parcel type for consistency across adapters.
|
|
61
|
+
*
|
|
62
|
+
* Maps canonical Parcel to Foxpost CreateParcelRequest (and carrier-specific type)
|
|
63
|
+
* Returns the parcel barcode as carrierId
|
|
64
|
+
*/
|
|
65
|
+
async createParcel(req, ctx) {
|
|
66
|
+
return createParcelImpl(req, ctx, (batchReq, batchCtx) => this.createParcels(batchReq, batchCtx));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create multiple parcels in one call
|
|
70
|
+
* Maps canonical Parcel array to Foxpost CreateParcelRequest and calls the
|
|
71
|
+
* Foxpost batch endpoint which accepts an array. Returns per-item CarrierResource
|
|
72
|
+
* so callers can handle partial failures.
|
|
73
|
+
*
|
|
74
|
+
* Validates both the incoming parcels and the mapped carrier-specific payloads.
|
|
75
|
+
*
|
|
76
|
+
* @returns CreateParcelsResponse with summary and per-item results
|
|
77
|
+
*/
|
|
78
|
+
async createParcels(req, ctx) {
|
|
79
|
+
return createParcelsImpl(req, ctx, this.resolveBaseUrl);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Create a label (generate PDF) for a parcel
|
|
83
|
+
*
|
|
84
|
+
* @param req CreateLabelRequest with parcelCarrierId (Foxpost barcode)
|
|
85
|
+
* @param ctx AdapterContext with HTTP client and logger
|
|
86
|
+
* @returns LabelResult with file mapping and page range
|
|
87
|
+
*/
|
|
88
|
+
async createLabel(req, ctx) {
|
|
89
|
+
return createLabelImpl(req, ctx, this.resolveBaseUrl);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create labels for multiple parcels in one batch call
|
|
93
|
+
*
|
|
94
|
+
* Generates a single PDF with all requested labels using Foxpost POST /api/label/{pageSize}
|
|
95
|
+
* Returns per-item results for tracking success/failure of each label
|
|
96
|
+
*
|
|
97
|
+
* @param req CreateLabelsRequest with array of parcelCarrierIds
|
|
98
|
+
* @param ctx AdapterContext with HTTP client and logger
|
|
99
|
+
* @returns CreateLabelsResponse with per-item results and summary
|
|
100
|
+
*/
|
|
101
|
+
async createLabels(req, ctx) {
|
|
102
|
+
return createLabelsImpl(req, ctx, this.resolveBaseUrl);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* NOT IMPLEMENTED: Foxpost doesn't support voiding labels
|
|
106
|
+
*/
|
|
107
|
+
async voidLabel(_labelId, _ctx) {
|
|
108
|
+
throw new NotImplementedError("VOID_LABEL", this.id);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Track a parcel by its clFoxId or uniqueBarcode using the new GET /api/tracking/{barcode} endpoint
|
|
112
|
+
*
|
|
113
|
+
* Returns normalized tracking information with all available traces in reverse chronological order
|
|
114
|
+
*
|
|
115
|
+
* To use test API, pass in request as:
|
|
116
|
+
* { trackingNumber: barcode, credentials: {...}, options?: { useTestApi: true } }
|
|
117
|
+
*/
|
|
118
|
+
async track(req, ctx) {
|
|
119
|
+
return trackImpl(req, ctx, this.resolveBaseUrl);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fetch list of Foxpost pickup points (APMs)
|
|
123
|
+
*
|
|
124
|
+
* Fetches the public JSON feed from https://cdn.foxpost.hu/foxplus.json
|
|
125
|
+
* which is updated hourly and contains all active APM locations.
|
|
126
|
+
*
|
|
127
|
+
* No authentication is required as this is a public feed.
|
|
128
|
+
*
|
|
129
|
+
* @param req FetchPickupPointsRequest (optional filters)
|
|
130
|
+
* @param ctx AdapterContext with HTTP client
|
|
131
|
+
* @returns FetchPickupPointsResponse with normalized pickup points
|
|
132
|
+
*/
|
|
133
|
+
async fetchPickupPoints(req, ctx) {
|
|
134
|
+
return fetchPickupPointsImpl(req, ctx);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* NOT IMPLEMENTED: Foxpost doesn't support pickup requests
|
|
138
|
+
*/
|
|
139
|
+
async requestPickup(_req, _ctx) {
|
|
140
|
+
throw new NotImplementedError("PICKUP", this.id);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* NOT IMPLEMENTED: Foxpost doesn't expose rate quotes
|
|
144
|
+
*/
|
|
145
|
+
async getRates(_req, _ctx) {
|
|
146
|
+
throw new NotImplementedError("RATES", this.id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export default FoxpostAdapter;
|
|
150
|
+
// Export validation helpers for use in external code (e.g., dev-server)
|
|
151
|
+
export { safeValidateTrackingRequest, safeValidateFoxpostTracking, validateFoxpostTracking, safeValidateFoxpostCredentials, validateFoxpostCredentials, } from './validation.js';
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mappers for Foxpost adapter
|
|
3
|
+
* Converts between canonical Shopickup types and Foxpost API types
|
|
4
|
+
*/
|
|
5
|
+
import type { Parcel, TrackingEvent } from "@shopickup/core";
|
|
6
|
+
import type { CreateParcelRequest as FoxpostParcelRequest, TrackDTO as FoxpostTrackDTO, TraceDTO as FoxpostTraceDTO } from '../types/generated.js';
|
|
7
|
+
import type { FoxpostParcel, FoxCreateParcelRequestItem } from '../validation.js';
|
|
8
|
+
declare const FOXPOST_SIZES: readonly ["xs", "s", "m", "l", "xl"];
|
|
9
|
+
type FoxpostSize = typeof FOXPOST_SIZES[number];
|
|
10
|
+
/**
|
|
11
|
+
* Map canonical Address (from Parcel.sender or Parcel.recipient) to Foxpost address format
|
|
12
|
+
*/
|
|
13
|
+
export declare function mapAddressToFoxpost(addr: {
|
|
14
|
+
name: string;
|
|
15
|
+
street: string;
|
|
16
|
+
city: string;
|
|
17
|
+
postalCode: string;
|
|
18
|
+
country: string;
|
|
19
|
+
phone?: string;
|
|
20
|
+
email?: string;
|
|
21
|
+
}): {
|
|
22
|
+
name: string;
|
|
23
|
+
phone: string;
|
|
24
|
+
email: string;
|
|
25
|
+
city?: string;
|
|
26
|
+
zip?: string;
|
|
27
|
+
address?: string;
|
|
28
|
+
country: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Determine parcel size based on dimensions or weight
|
|
32
|
+
* Foxpost sizes: xs, s, m, l, xl
|
|
33
|
+
*/
|
|
34
|
+
export declare function determineFoxpostSize(parcel: Parcel): FoxpostSize | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Map canonical Parcel to Foxpost CreateParcelRequest (strongly-typed)
|
|
37
|
+
* Returns typed FoxCreateParcelRequestItem with lenient validation support
|
|
38
|
+
*
|
|
39
|
+
* Handles both HOME delivery (full address) and PICKUP_POINT delivery (APM/locker)
|
|
40
|
+
*/
|
|
41
|
+
export declare function mapParcelToFoxpostRequest(parcel: Parcel, options?: {
|
|
42
|
+
isWeb?: boolean;
|
|
43
|
+
isRedirect?: boolean;
|
|
44
|
+
}): FoxCreateParcelRequestItem;
|
|
45
|
+
/**
|
|
46
|
+
* Map canonical Parcel to Foxpost CreateParcelRequest
|
|
47
|
+
* Parcel contains complete shipping details (sender, recipient, weight, service, etc.)
|
|
48
|
+
*
|
|
49
|
+
* Handles both HOME delivery (full address) and PICKUP_POINT delivery (APM/locker)
|
|
50
|
+
*/
|
|
51
|
+
export declare function mapParcelToFoxpost(parcel: Parcel, options?: {
|
|
52
|
+
isWeb?: boolean;
|
|
53
|
+
isRedirect?: boolean;
|
|
54
|
+
}): FoxpostParcelRequest & {
|
|
55
|
+
destination?: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Map Foxpost tracking status code to canonical TrackingStatus
|
|
59
|
+
* Uses comprehensive status mapping from trackStatus.ts
|
|
60
|
+
* Unknown codes default to PENDING.
|
|
61
|
+
*/
|
|
62
|
+
export declare function mapFoxpostStatusToCanonical(foxpostStatus: string): "PENDING" | "IN_TRANSIT" | "OUT_FOR_DELIVERY" | "DELIVERED" | "EXCEPTION" | "RETURNED" | "CANCELLED";
|
|
63
|
+
/**
|
|
64
|
+
* Map Foxpost TrackDTO to canonical TrackingEvent
|
|
65
|
+
*
|
|
66
|
+
* Normalizes the Foxpost status code to a canonical TrackingStatus while preserving
|
|
67
|
+
* the original carrier-specific code in `carrierStatusCode` for debugging and carrier-specific logic.
|
|
68
|
+
*/
|
|
69
|
+
export declare function mapFoxpostTrackToCanonical(track: FoxpostTrackDTO): TrackingEvent;
|
|
70
|
+
/**
|
|
71
|
+
* Map Foxpost TraceDTO (from new /api/tracking/{barcode} endpoint) to canonical TrackingEvent
|
|
72
|
+
* TraceDTO is returned in reverse chronological order (latest first) from the API
|
|
73
|
+
*
|
|
74
|
+
* Normalizes the Foxpost status code to a canonical TrackingStatus while preserving
|
|
75
|
+
* the original carrier-specific code in `carrierStatusCode`.
|
|
76
|
+
*
|
|
77
|
+
* Includes both English and Hungarian human-readable descriptions from the status map.
|
|
78
|
+
*
|
|
79
|
+
* Accepts both string and Date types for statusDate to support both raw API responses
|
|
80
|
+
* and validated Zod-parsed responses (which transform to Date).
|
|
81
|
+
*
|
|
82
|
+
* Example mapping:
|
|
83
|
+
* - Foxpost "CREATE" -> canonical "PENDING" (carrierStatusCode: "CREATE", description: "Order created")
|
|
84
|
+
* - Foxpost "HDINTRANSIT" -> canonical "OUT_FOR_DELIVERY" (carrierStatusCode: "HDINTRANSIT", description: "Out for home delivery")
|
|
85
|
+
* - Foxpost "RECEIVE" -> canonical "DELIVERED" (carrierStatusCode: "RECEIVE", description: "Delivered to recipient")
|
|
86
|
+
*/
|
|
87
|
+
export declare function mapFoxpostTraceToCanonical(trace: FoxpostTraceDTO | {
|
|
88
|
+
statusDate: string | Date;
|
|
89
|
+
status?: string;
|
|
90
|
+
shortName?: string;
|
|
91
|
+
longName?: string;
|
|
92
|
+
}): TrackingEvent;
|
|
93
|
+
/**
|
|
94
|
+
* Map canonical Parcel to Foxpost carrier-specific parcel (HD or APM)
|
|
95
|
+
* Discriminates based on Delivery type in the parcel
|
|
96
|
+
*
|
|
97
|
+
* @param parcel - Canonical parcel with full shipping details
|
|
98
|
+
* @param options - Additional mapping options (COD, comment, etc.)
|
|
99
|
+
* @returns FoxpostParcelHD | FoxpostParcelAPM with type discriminator set
|
|
100
|
+
*/
|
|
101
|
+
export declare function mapParcelToFoxpostCarrierType(parcel: Parcel, options?: {
|
|
102
|
+
cod?: number;
|
|
103
|
+
comment?: string;
|
|
104
|
+
deliveryNote?: string;
|
|
105
|
+
fragile?: boolean;
|
|
106
|
+
}): FoxpostParcel;
|
|
107
|
+
export {};
|
|
108
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mappers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,KAAK,EACV,mBAAmB,IAAI,oBAAoB,EAC3C,QAAQ,IAAI,eAAe,EAC3B,QAAQ,IAAI,eAAe,EAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,aAAa,EAAsB,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAGtG,QAAA,MAAM,aAAa,sCAAuC,CAAC;AAC3D,KAAK,WAAW,GAAG,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;AAEhD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAC9J,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAUA;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAwB5E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACjB,GACL,0BAA0B,CAuD5B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACjB,GACL,oBAAoB,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CA8CjD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,GACpB,SAAS,GAAG,YAAY,GAAG,kBAAkB,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,WAAW,CAGtG;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,eAAe,GACrB,aAAa,CAQf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,eAAe,GAAG;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7G,aAAa,CAuBf;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,aAAa,CAoDf"}
|