@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.
Files changed (47) hide show
  1. package/README.md +48 -0
  2. package/dist/capabilities/index.d.ts +9 -0
  3. package/dist/capabilities/index.d.ts.map +1 -0
  4. package/dist/capabilities/index.js +8 -0
  5. package/dist/capabilities/label.d.ts +27 -0
  6. package/dist/capabilities/label.d.ts.map +1 -0
  7. package/dist/capabilities/label.js +370 -0
  8. package/dist/capabilities/parcels.d.ts +21 -0
  9. package/dist/capabilities/parcels.d.ts.map +1 -0
  10. package/dist/capabilities/parcels.js +233 -0
  11. package/dist/capabilities/pickup-points.d.ts +38 -0
  12. package/dist/capabilities/pickup-points.d.ts.map +1 -0
  13. package/dist/capabilities/pickup-points.js +225 -0
  14. package/dist/capabilities/track.d.ts +16 -0
  15. package/dist/capabilities/track.d.ts.map +1 -0
  16. package/dist/capabilities/track.js +99 -0
  17. package/dist/client/index.d.ts +17 -0
  18. package/dist/client/index.d.ts.map +1 -0
  19. package/dist/client/index.js +30 -0
  20. package/dist/errors.d.ts +34 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +165 -0
  23. package/dist/index.d.ts +119 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +151 -0
  26. package/dist/mappers/index.d.ts +108 -0
  27. package/dist/mappers/index.d.ts.map +1 -0
  28. package/dist/mappers/index.js +270 -0
  29. package/dist/mappers/trackStatus.d.ts +58 -0
  30. package/dist/mappers/trackStatus.d.ts.map +1 -0
  31. package/dist/mappers/trackStatus.js +290 -0
  32. package/dist/types/generated.d.ts +177 -0
  33. package/dist/types/generated.d.ts.map +1 -0
  34. package/dist/types/generated.js +9 -0
  35. package/dist/types/index.d.ts +7 -0
  36. package/dist/types/index.d.ts.map +1 -0
  37. package/dist/types/index.js +6 -0
  38. package/dist/utils/httpUtils.d.ts +18 -0
  39. package/dist/utils/httpUtils.d.ts.map +1 -0
  40. package/dist/utils/httpUtils.js +33 -0
  41. package/dist/utils/resolveBaseUrl.d.ts +23 -0
  42. package/dist/utils/resolveBaseUrl.d.ts.map +1 -0
  43. package/dist/utils/resolveBaseUrl.js +19 -0
  44. package/dist/validation.d.ts +1723 -0
  45. package/dist/validation.d.ts.map +1 -0
  46. package/dist/validation.js +799 -0
  47. package/package.json +68 -0
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # @shopickup/adapters-foxpost
2
+
3
+ Foxpost 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_LABEL`
12
+ - `TRACK`
13
+ - `LIST_PICKUP_POINTS`
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pnpm add @shopickup/adapters-foxpost @shopickup/core
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```ts
24
+ import { FoxpostAdapter } from '@shopickup/adapters-foxpost';
25
+ import { executeCreateLabelFlow } from '@shopickup/core';
26
+
27
+ const adapter = new FoxpostAdapter('https://webapi.foxpost.hu');
28
+
29
+ const result = await executeCreateLabelFlow({
30
+ adapter,
31
+ shipment: {
32
+ id: 'order-001',
33
+ sender: { name: 'Shop', street: 'Main', city: 'Budapest', postalCode: '1011', country: 'HU' },
34
+ recipient: { name: 'Customer', street: 'Fo utca 2', city: 'Siofok', postalCode: '8600', country: 'HU' },
35
+ service: 'standard',
36
+ totalWeight: 1200,
37
+ createdAt: new Date(),
38
+ updatedAt: new Date(),
39
+ },
40
+ parcels: [{ id: 'parcel-1', weight: 1200 }],
41
+ credentials: { apiKey: 'your-foxpost-api-key' },
42
+ context: { http: yourHttpClient, logger: console },
43
+ });
44
+ ```
45
+
46
+ ## Status
47
+
48
+ Published as `0.x.x` while the adapter API is still evolving.
@@ -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 { track } from './track.js';
8
+ export { fetchPickupPoints } from './pickup-points.js';
9
+ //# 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;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,8 @@
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 { track } from './track.js';
8
+ export { fetchPickupPoints } from './pickup-points.js';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Foxpost Adapter: Label Generation Capability
3
+ * Handles CREATE_LABEL and CREATE_LABELS operations
4
+ */
5
+ import type { AdapterContext, CreateLabelResponse, CreateLabelsResponse } from "@shopickup/core";
6
+ import type { CreateLabelRequestFoxpost, CreateLabelsRequestFoxpost } from "../validation.js";
7
+ import type { ResolveBaseUrl } from "../utils/resolveBaseUrl.js";
8
+ /**
9
+ * Create a label (generate PDF) for a single parcel
10
+ * Delegates to createLabels to reuse batching logic
11
+ *
12
+ * Returns Promise<LabelResult> with file mapping and metadata
13
+ */
14
+ export declare function createLabel(req: CreateLabelRequestFoxpost, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<CreateLabelResponse>;
15
+ /**
16
+ * Create labels for multiple parcels in one call
17
+ *
18
+ * Foxpost POST /api/label/{pageSize} endpoint:
19
+ * - Takes array of parcel IDs (barcodes)
20
+ * - Returns PDF with all labels (optionally concatenated based on pageSize)
21
+ * - For A7 size on A4 page, supports startPos parameter (1-7)
22
+ *
23
+ * Returns structured response with files array and per-item results
24
+ * Foxpost returns one PDF (combined), so all results reference the same file
25
+ */
26
+ export declare function createLabels(req: CreateLabelsRequestFoxpost, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<CreateLabelsResponse>;
27
+ //# sourceMappingURL=label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../src/capabilities/label.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAET,cAAc,EACf,mBAAmB,EAClB,oBAAoB,EAGtB,MAAM,iBAAiB,CAAC;AASzB,OAAO,KAAK,EACV,yBAAyB,EACzB,0BAA0B,EAC3B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAkEjE;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,yBAAyB,EAC9B,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,mBAAmB,CAAC,CAkD9B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,0BAA0B,EAC/B,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAqS/B"}
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Foxpost Adapter: Label Generation Capability
3
+ * Handles CREATE_LABEL and CREATE_LABELS operations
4
+ */
5
+ import { CarrierError, errorToLog, serializeForLog } from "@shopickup/core";
6
+ import { translateFoxpostError } from '../errors.js';
7
+ import { safeValidateCreateLabelRequest, safeValidateCreateLabelsRequest, safeValidateFoxpostLabelPdfRaw, safeValidateFoxpostApiError, } from "../validation.js";
8
+ import { buildFoxpostBinaryHeaders } from '../utils/httpUtils.js';
9
+ import { URLSearchParams } from "node:url";
10
+ import { randomUUID } from "node:crypto";
11
+ const BLOB_FIELDS = new Set(['label', 'labelBase64']);
12
+ function isSerializedBuffer(value) {
13
+ return (!!value &&
14
+ typeof value === 'object' &&
15
+ value.type === 'Buffer' &&
16
+ Array.isArray(value.data));
17
+ }
18
+ function summarizeBufferLike(value) {
19
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
20
+ return {
21
+ omittedBinary: true,
22
+ byteLength: value.byteLength,
23
+ note: 'binary payload omitted from rawCarrierResponse',
24
+ };
25
+ }
26
+ return {
27
+ omittedBinary: true,
28
+ byteLength: value.data.length,
29
+ note: 'binary payload omitted from rawCarrierResponse',
30
+ };
31
+ }
32
+ function sanitizeRawValue(value, keyHint) {
33
+ if (typeof value === 'string') {
34
+ if (keyHint && BLOB_FIELDS.has(keyHint)) {
35
+ return `[truncated ${keyHint}; length=${value.length}]`;
36
+ }
37
+ return value;
38
+ }
39
+ if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
40
+ return summarizeBufferLike(value);
41
+ }
42
+ if (isSerializedBuffer(value)) {
43
+ return summarizeBufferLike(value);
44
+ }
45
+ if (Array.isArray(value)) {
46
+ return value.map((item) => sanitizeRawValue(item));
47
+ }
48
+ if (value && typeof value === 'object') {
49
+ const out = {};
50
+ for (const [key, nested] of Object.entries(value)) {
51
+ out[key] = sanitizeRawValue(nested, key);
52
+ }
53
+ return out;
54
+ }
55
+ return value;
56
+ }
57
+ function sanitizeRawCarrierResponse(raw) {
58
+ return sanitizeRawValue(raw);
59
+ }
60
+ /**
61
+ * Create a label (generate PDF) for a single parcel
62
+ * Delegates to createLabels to reuse batching logic
63
+ *
64
+ * Returns Promise<LabelResult> with file mapping and metadata
65
+ */
66
+ export async function createLabel(req, ctx, resolveBaseUrl) {
67
+ // Validate request format and credentials
68
+ const validated = safeValidateCreateLabelRequest(req);
69
+ if (!validated.success) {
70
+ throw new CarrierError(`Invalid request: ${validated.error.message}`, "Validation", { raw: serializeForLog(validated.error) });
71
+ }
72
+ // Convert single label request to batch request
73
+ const batchReq = {
74
+ parcelCarrierIds: [validated.data.parcelCarrierId],
75
+ credentials: req.credentials,
76
+ options: req.options,
77
+ };
78
+ // Delegate to batch implementation
79
+ const response = await createLabels(batchReq, ctx, resolveBaseUrl);
80
+ // Extract first (only) result
81
+ if (!response || !Array.isArray(response.results)) {
82
+ throw new CarrierError("Unexpected response shape from createLabels", "Transient", { raw: serializeForLog(response) });
83
+ }
84
+ const results = response.results;
85
+ if (results.length === 0) {
86
+ throw new CarrierError("createLabels returned an empty results array", "Transient", { raw: serializeForLog(response) });
87
+ }
88
+ // Return the first (only) label result
89
+ const result = results[0];
90
+ const file = result.fileId
91
+ ? response.files?.find((candidate) => candidate.id === result.fileId)
92
+ : undefined;
93
+ return {
94
+ ...result,
95
+ file,
96
+ rawCarrierResponse: response.rawCarrierResponse,
97
+ };
98
+ }
99
+ /**
100
+ * Create labels for multiple parcels in one call
101
+ *
102
+ * Foxpost POST /api/label/{pageSize} endpoint:
103
+ * - Takes array of parcel IDs (barcodes)
104
+ * - Returns PDF with all labels (optionally concatenated based on pageSize)
105
+ * - For A7 size on A4 page, supports startPos parameter (1-7)
106
+ *
107
+ * Returns structured response with files array and per-item results
108
+ * Foxpost returns one PDF (combined), so all results reference the same file
109
+ */
110
+ export async function createLabels(req, ctx, resolveBaseUrl) {
111
+ try {
112
+ // Validate request format and credentials
113
+ const validated = safeValidateCreateLabelsRequest(req);
114
+ if (!validated.success) {
115
+ throw new CarrierError(`Invalid request: ${validated.error.message}`, "Validation", { raw: validated.error });
116
+ }
117
+ if (!ctx.http) {
118
+ throw new CarrierError("HTTP client not provided in context", "Permanent");
119
+ }
120
+ if (!Array.isArray(req.parcelCarrierIds) || req.parcelCarrierIds.length === 0) {
121
+ return {
122
+ results: [],
123
+ files: [],
124
+ successCount: 0,
125
+ failureCount: 0,
126
+ totalCount: 0,
127
+ allSucceeded: false,
128
+ allFailed: false,
129
+ someFailed: false,
130
+ summary: "No parcels to process",
131
+ };
132
+ }
133
+ // Normalize public request options to adapter internal options.
134
+ const internalOptions = {
135
+ useTestApi: validated.data.options?.useTestApi ?? false,
136
+ size: validated.data.options?.size ?? "A7",
137
+ startPos: validated.data.options?.foxpost?.startPos,
138
+ isPortrait: validated.data.options?.foxpost?.isPortrait ?? false,
139
+ };
140
+ const baseUrl = resolveBaseUrl({ useTestApi: internalOptions.useTestApi });
141
+ // Construct URL with page size and optional params
142
+ const params = new URLSearchParams();
143
+ if (internalOptions.startPos !== undefined && internalOptions.startPos !== null) {
144
+ params.set('startPos', String(internalOptions.startPos));
145
+ }
146
+ if (internalOptions.isPortrait !== undefined && internalOptions.isPortrait !== null) {
147
+ params.set('isPortrait', String(internalOptions.isPortrait));
148
+ }
149
+ const url = `${baseUrl}/api/label/${internalOptions.size}${params.toString() ? `?${params.toString()}` : ''}`;
150
+ ctx.logger?.debug("Foxpost: Creating labels batch", {
151
+ testMode: internalOptions.useTestApi,
152
+ count: req.parcelCarrierIds.length,
153
+ size: internalOptions.size,
154
+ startPos: internalOptions.startPos,
155
+ isPortrait: internalOptions.isPortrait,
156
+ });
157
+ try {
158
+ // Make request to Foxpost label API
159
+ // Response is PDF binary data
160
+ const httpResponse = await ctx.http.post(url, validated.data.parcelCarrierIds, {
161
+ headers: buildFoxpostBinaryHeaders(validated.data.credentials),
162
+ responseType: "arraybuffer",
163
+ });
164
+ // Extract buffer from normalized HttpResponse
165
+ const pdfBuffer = httpResponse.body;
166
+ // Validate PDF binary response is non-empty
167
+ const pdfValidation = safeValidateFoxpostLabelPdfRaw(pdfBuffer);
168
+ if (!pdfValidation.success) {
169
+ throw new CarrierError(`Invalid PDF response: ${pdfValidation.error.message}`, "Transient", { raw: serializeForLog(pdfValidation.error) });
170
+ }
171
+ // Get byte length (works for Buffer and Uint8Array)
172
+ const byteLength = pdfBuffer instanceof Buffer ? pdfBuffer.byteLength :
173
+ pdfBuffer instanceof Uint8Array ? pdfBuffer.byteLength :
174
+ 0;
175
+ // Create file resource for the single PDF
176
+ const fileId = randomUUID();
177
+ const file = {
178
+ id: fileId,
179
+ contentType: "application/pdf",
180
+ byteLength,
181
+ pages: req.parcelCarrierIds.length, // One page per label (Foxpost behavior)
182
+ orientation: internalOptions.isPortrait === false ? 'landscape' : 'portrait',
183
+ metadata: {
184
+ size: internalOptions.size,
185
+ isPortrait: internalOptions.isPortrait,
186
+ barcodeCount: req.parcelCarrierIds.length,
187
+ combined: true, // All labels in one file
188
+ },
189
+ // Attach raw bytes so dev-server responses can include file bytes directly
190
+ rawBytes: pdfBuffer,
191
+ };
192
+ ctx.logger?.info("Foxpost: Labels created successfully", {
193
+ count: req.parcelCarrierIds.length,
194
+ size: internalOptions.size,
195
+ testMode: internalOptions.useTestApi,
196
+ });
197
+ // Create per-item results, all referencing the same file
198
+ const results = req.parcelCarrierIds.map((barcode, idx) => ({
199
+ inputId: barcode,
200
+ status: "created",
201
+ fileId,
202
+ pageRange: { start: idx + 1, end: idx + 1 }, // One page per label
203
+ raw: {
204
+ barcode,
205
+ format: "PDF",
206
+ pageSize: internalOptions.size,
207
+ startPos: internalOptions.startPos,
208
+ pageNumber: idx + 1,
209
+ },
210
+ }));
211
+ return {
212
+ results,
213
+ files: [file],
214
+ successCount: results.length,
215
+ failureCount: 0,
216
+ totalCount: results.length,
217
+ allSucceeded: true,
218
+ allFailed: false,
219
+ someFailed: false,
220
+ summary: `All ${results.length} labels generated successfully`,
221
+ // Preserve status/headers but strip embedded label/blob payloads.
222
+ rawCarrierResponse: sanitizeRawCarrierResponse(serializeForLog(httpResponse)),
223
+ };
224
+ }
225
+ catch (labelError) {
226
+ // Try to parse error response as Foxpost ApiError
227
+ let errorMessage = `Failed to generate label: ${labelError?.message || "Unknown error"}`;
228
+ let errorCategory = 'Transient';
229
+ // If labelError is from Foxpost API and contains status/error info, extract it
230
+ if (labelError instanceof CarrierError) {
231
+ // Already a CarrierError from validation, propagate it
232
+ throw labelError;
233
+ }
234
+ // Try to extract HTTP status from axios-like error
235
+ const httpStatus = labelError?.response?.status;
236
+ ctx.logger?.debug("Foxpost error analysis", {
237
+ hasResponse: !!labelError?.response,
238
+ httpStatus,
239
+ dataType: labelError?.response?.data?.constructor?.name,
240
+ isBuffer: Buffer.isBuffer(labelError?.response?.data),
241
+ });
242
+ if (httpStatus) {
243
+ // Attempt to parse error body as JSON if available
244
+ try {
245
+ let errorBody = labelError?.response?.data;
246
+ if (errorBody) {
247
+ ctx.logger?.debug("Raw error body before parsing", {
248
+ type: errorBody?.constructor?.name,
249
+ isBuffer: Buffer.isBuffer(errorBody),
250
+ isUint8Array: errorBody instanceof Uint8Array,
251
+ byteLength: errorBody?.length || errorBody?.byteLength,
252
+ });
253
+ // If error body is a Buffer, decode it to string first
254
+ if (Buffer.isBuffer(errorBody)) {
255
+ const decoded = errorBody.toString('utf-8');
256
+ ctx.logger?.debug("Decoded buffer to string", { decoded });
257
+ errorBody = JSON.parse(decoded);
258
+ }
259
+ else if (errorBody instanceof Uint8Array) {
260
+ errorBody = JSON.parse(new TextDecoder().decode(errorBody));
261
+ }
262
+ ctx.logger?.debug("Parsed error body", { errorBody });
263
+ // Try to parse as Foxpost ApiError
264
+ const apiErrorValidation = safeValidateFoxpostApiError(errorBody);
265
+ if (apiErrorValidation.success) {
266
+ const apiError = apiErrorValidation.data;
267
+ // Use error code as message if available
268
+ if (apiError.error) {
269
+ errorMessage = apiError.error;
270
+ ctx.logger?.debug("Extracted error message from API response", { message: errorMessage });
271
+ }
272
+ // Map HTTP status to error category
273
+ if (httpStatus === 400) {
274
+ errorCategory = /not[_ -]?found/i.test(String(apiError.error || errorMessage))
275
+ ? 'NotFound'
276
+ : 'Validation';
277
+ }
278
+ else if (httpStatus === 401 || httpStatus === 403) {
279
+ errorCategory = 'Auth';
280
+ }
281
+ else if (httpStatus >= 500) {
282
+ errorCategory = 'Transient';
283
+ }
284
+ }
285
+ else {
286
+ // Validation failed, log the issue
287
+ ctx.logger?.debug("Failed to validate Foxpost API error response", {
288
+ validation: apiErrorValidation.error,
289
+ attemptedBody: errorBody,
290
+ });
291
+ }
292
+ }
293
+ }
294
+ catch (parseError) {
295
+ // If parsing fails, log and use default error category based on status
296
+ ctx.logger?.debug("Failed to parse error body", {
297
+ error: parseError?.message,
298
+ });
299
+ if (httpStatus === 400) {
300
+ errorCategory = 'Validation';
301
+ }
302
+ else if (httpStatus === 401 || httpStatus === 403) {
303
+ errorCategory = 'Auth';
304
+ }
305
+ else if (httpStatus >= 500) {
306
+ errorCategory = 'Transient';
307
+ }
308
+ }
309
+ }
310
+ // If PDF generation fails, return error results for all barcodes
311
+ ctx.logger?.error("Foxpost: Label generation failed", {
312
+ count: req.parcelCarrierIds.length,
313
+ size: internalOptions.size,
314
+ error: errorToLog(labelError),
315
+ });
316
+ // Return failed results for all parcels
317
+ const errorObj = {
318
+ code: "LABEL_GENERATION_FAILED",
319
+ message: errorMessage,
320
+ };
321
+ ctx.logger?.debug("Creating error object", {
322
+ code: errorObj.code,
323
+ message: errorObj.message,
324
+ stringified: JSON.stringify(errorObj),
325
+ });
326
+ const results = req.parcelCarrierIds.map((barcode) => {
327
+ const result = {
328
+ inputId: barcode,
329
+ status: "failed",
330
+ errors: [errorObj],
331
+ raw: { barcode, error: serializeForLog(labelError) },
332
+ };
333
+ ctx.logger?.debug("Result object created", {
334
+ inputId: result.inputId,
335
+ status: result.status,
336
+ errorsLength: result.errors?.length,
337
+ firstError: result.errors?.[0],
338
+ });
339
+ return result;
340
+ });
341
+ ctx.logger?.debug("Error results being returned", {
342
+ sample: results[0],
343
+ errorMessage,
344
+ errorCategory,
345
+ });
346
+ return {
347
+ results,
348
+ files: [],
349
+ successCount: 0,
350
+ failureCount: results.length,
351
+ totalCount: results.length,
352
+ allSucceeded: false,
353
+ allFailed: true,
354
+ someFailed: false,
355
+ summary: `All ${results.length} labels failed`,
356
+ rawCarrierResponse: sanitizeRawCarrierResponse({ error: serializeForLog(labelError) }),
357
+ };
358
+ }
359
+ }
360
+ catch (error) {
361
+ if (error instanceof CarrierError) {
362
+ throw error;
363
+ }
364
+ ctx.logger?.error("Foxpost: Error creating labels", {
365
+ count: req.parcelCarrierIds.length,
366
+ error: errorToLog(error),
367
+ });
368
+ throw translateFoxpostError(error);
369
+ }
370
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Foxpost Adapter: Parcel Creation Capabilities
3
+ * Handles CREATE_PARCEL and CREATE_PARCELS operations
4
+ */
5
+ import type { CarrierResource, AdapterContext, CreateParcelRequest, CreateParcelsRequest, CreateParcelsResponse } from "@shopickup/core";
6
+ import type { ResolveBaseUrl } from '../utils/resolveBaseUrl.js';
7
+ /**
8
+ * Create a single parcel in Foxpost
9
+ * Delegates to createParcels to reuse batching logic
10
+ */
11
+ export declare function createParcel(req: CreateParcelRequest, ctx: AdapterContext, createParcelsImpl: (req: CreateParcelsRequest, ctx: AdapterContext) => Promise<CreateParcelsResponse>): Promise<CarrierResource>;
12
+ /**
13
+ * Create multiple parcels in one call
14
+ * Maps canonical Parcel array to Foxpost CreateParcelRequest and calls the
15
+ * Foxpost batch endpoint which accepts an array. Returns per-item CarrierResource
16
+ * so callers can handle partial failures.
17
+ *
18
+ * @returns CreateParcelsResponse with summary and per-item results
19
+ */
20
+ export declare function createParcels(req: CreateParcelsRequest, ctx: AdapterContext, resolveBaseUrl: ResolveBaseUrl): Promise<CreateParcelsResponse>;
21
+ //# sourceMappingURL=parcels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parcels.d.ts","sourceRoot":"","sources":["../../src/capabilities/parcels.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,eAAe,EAGf,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AASzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,mBAAmB,EACxB,GAAG,EAAE,cAAc,EACnB,iBAAiB,EAAE,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,qBAAqB,CAAC,GACpG,OAAO,CAAC,eAAe,CAAC,CA2C1B;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,oBAAoB,EACzB,GAAG,EAAE,cAAc,EACnB,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAmOhC"}