@jazzdev/dpd-local-sdk 1.0.13 → 1.1.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/dist/index.d.mts +43 -1
- package/dist/index.d.ts +43 -1
- package/dist/index.js +198 -3
- package/dist/index.mjs +196 -3
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -260,6 +260,13 @@ interface CreateShipmentResult {
|
|
|
260
260
|
labelUrl?: string;
|
|
261
261
|
error?: string;
|
|
262
262
|
errorCode?: string;
|
|
263
|
+
errorDetails?: {
|
|
264
|
+
status: number;
|
|
265
|
+
statusText: string;
|
|
266
|
+
error: unknown;
|
|
267
|
+
raw: string;
|
|
268
|
+
endpoint: string;
|
|
269
|
+
};
|
|
263
270
|
}
|
|
264
271
|
interface GenerateLabelParams {
|
|
265
272
|
shipmentId: string | number;
|
|
@@ -316,6 +323,24 @@ interface LogFilters {
|
|
|
316
323
|
endDate?: Date;
|
|
317
324
|
limit?: number;
|
|
318
325
|
}
|
|
326
|
+
interface GeoSessionStorage {
|
|
327
|
+
get(): Promise<{
|
|
328
|
+
geoSession: string;
|
|
329
|
+
expiry: Date;
|
|
330
|
+
} | null>;
|
|
331
|
+
set(geoSession: string, expiry: Date): Promise<void>;
|
|
332
|
+
clear(): Promise<void>;
|
|
333
|
+
}
|
|
334
|
+
declare class InMemoryGeoSessionStorage implements GeoSessionStorage {
|
|
335
|
+
private geoSession;
|
|
336
|
+
private expiry;
|
|
337
|
+
get(): Promise<{
|
|
338
|
+
geoSession: string;
|
|
339
|
+
expiry: Date;
|
|
340
|
+
} | null>;
|
|
341
|
+
set(geoSession: string, expiry: Date): Promise<void>;
|
|
342
|
+
clear(): Promise<void>;
|
|
343
|
+
}
|
|
319
344
|
|
|
320
345
|
declare const DPD_API: {
|
|
321
346
|
readonly BASE_URL: "https://api.dpdlocal.co.uk";
|
|
@@ -408,6 +433,23 @@ declare function testConnection(credentials: DPDCredentials): Promise<{
|
|
|
408
433
|
success: boolean;
|
|
409
434
|
message: string;
|
|
410
435
|
}>;
|
|
436
|
+
declare class GeoSessionManager {
|
|
437
|
+
private credentials;
|
|
438
|
+
private storage;
|
|
439
|
+
private maxRetries;
|
|
440
|
+
private baseRetryDelay;
|
|
441
|
+
constructor(credentials: DPDCredentials, storage?: GeoSessionStorage, options?: {
|
|
442
|
+
maxRetries?: number;
|
|
443
|
+
baseRetryDelay?: number;
|
|
444
|
+
});
|
|
445
|
+
getValidGeoSession(): Promise<string>;
|
|
446
|
+
refreshGeoSession(): Promise<string>;
|
|
447
|
+
clearGeoSession(): Promise<void>;
|
|
448
|
+
private isSessionValid;
|
|
449
|
+
private authenticateWithRetry;
|
|
450
|
+
private authenticate;
|
|
451
|
+
private delay;
|
|
452
|
+
}
|
|
411
453
|
|
|
412
454
|
declare function createShipment(credentials: DPDCredentials, params: CreateShipmentParams, businessConfig: any): Promise<CreateShipmentResult>;
|
|
413
455
|
declare function generateLabel(credentials: DPDCredentials, params: GenerateLabelParams): Promise<GenerateLabelResult>;
|
|
@@ -498,4 +540,4 @@ declare function loggedOperation<T>(params: {
|
|
|
498
540
|
headers?: Record<string, string>;
|
|
499
541
|
}>): Promise<T>;
|
|
500
542
|
|
|
501
|
-
export { type BusinessConfig, type CreateShipmentParams, type CreateShipmentResult, type DPDAddress, type DPDAuthResponse, type DPDConsignment, type DPDContact, type DPDCredentials, type DPDError, type DPDLabelRequest, type DPDLabelResponse, type DPDLogDocument, type DPDModuleConfig, type DPDParcel, _default as DPDService, type DPDServiceCode, type DPDShipmentRequest, type DPDShipmentResponse, DPD_API, type DatabaseAdapter, type GenerateLabelParams, type GenerateLabelResult, type LabelConfig, type LogFilters, type NotificationConfig, type PricingConfig, SERVICE_DESCRIPTIONS, SERVICE_NAMES, type SavedAddress, type ServiceConfig, type ShipmentStatus, type ShipmentStatusUpdate, type ShippingData, type StorageAdapter, type TimestampType, type TrackShipmentParams, type TrackShipmentResult, type ValidateAddressParams, type ValidateAddressResult, authenticate, authenticatedRequest, calculateDPDCost, calculateDeliveryFee, calculateParcels, clearGeoSession, configureLogger, createCompleteShipment, createDPDConfig, createShipment, deleteSavedAddress, generateAndUploadLabel, generateConsignmentRef, generateLabel, getAuthStatus, getEstimatedDeliveryDate, getGeoSession, getLabelUrl, getNextCollectionDate, getSavedAddress, getSavedAddresses, getServiceDescription, getServiceName, getTokenExpiry, getTrackingUrl, hasValidToken, isValidServiceCode, logOperation, loggedOperation, meetsMinimumOrderValue, qualifiesForFreeDelivery, regenerateLabel, saveAddress, setLoggerAdapter, startTimer, testConnection, testDPDConnection, trackShipment, updateSavedAddress, validateAddress, validateDeliveryAddress, validateServiceCode };
|
|
543
|
+
export { type BusinessConfig, type CreateShipmentParams, type CreateShipmentResult, type DPDAddress, type DPDAuthResponse, type DPDConsignment, type DPDContact, type DPDCredentials, type DPDError, type DPDLabelRequest, type DPDLabelResponse, type DPDLogDocument, type DPDModuleConfig, type DPDParcel, _default as DPDService, type DPDServiceCode, type DPDShipmentRequest, type DPDShipmentResponse, DPD_API, type DatabaseAdapter, type GenerateLabelParams, type GenerateLabelResult, GeoSessionManager, type GeoSessionStorage, InMemoryGeoSessionStorage, type LabelConfig, type LogFilters, type NotificationConfig, type PricingConfig, SERVICE_DESCRIPTIONS, SERVICE_NAMES, type SavedAddress, type ServiceConfig, type ShipmentStatus, type ShipmentStatusUpdate, type ShippingData, type StorageAdapter, type TimestampType, type TrackShipmentParams, type TrackShipmentResult, type ValidateAddressParams, type ValidateAddressResult, authenticate, authenticatedRequest, calculateDPDCost, calculateDeliveryFee, calculateParcels, clearGeoSession, configureLogger, createCompleteShipment, createDPDConfig, createShipment, deleteSavedAddress, generateAndUploadLabel, generateConsignmentRef, generateLabel, getAuthStatus, getEstimatedDeliveryDate, getGeoSession, getLabelUrl, getNextCollectionDate, getSavedAddress, getSavedAddresses, getServiceDescription, getServiceName, getTokenExpiry, getTrackingUrl, hasValidToken, isValidServiceCode, logOperation, loggedOperation, meetsMinimumOrderValue, qualifiesForFreeDelivery, regenerateLabel, saveAddress, setLoggerAdapter, startTimer, testConnection, testDPDConnection, trackShipment, updateSavedAddress, validateAddress, validateDeliveryAddress, validateServiceCode };
|
package/dist/index.d.ts
CHANGED
|
@@ -260,6 +260,13 @@ interface CreateShipmentResult {
|
|
|
260
260
|
labelUrl?: string;
|
|
261
261
|
error?: string;
|
|
262
262
|
errorCode?: string;
|
|
263
|
+
errorDetails?: {
|
|
264
|
+
status: number;
|
|
265
|
+
statusText: string;
|
|
266
|
+
error: unknown;
|
|
267
|
+
raw: string;
|
|
268
|
+
endpoint: string;
|
|
269
|
+
};
|
|
263
270
|
}
|
|
264
271
|
interface GenerateLabelParams {
|
|
265
272
|
shipmentId: string | number;
|
|
@@ -316,6 +323,24 @@ interface LogFilters {
|
|
|
316
323
|
endDate?: Date;
|
|
317
324
|
limit?: number;
|
|
318
325
|
}
|
|
326
|
+
interface GeoSessionStorage {
|
|
327
|
+
get(): Promise<{
|
|
328
|
+
geoSession: string;
|
|
329
|
+
expiry: Date;
|
|
330
|
+
} | null>;
|
|
331
|
+
set(geoSession: string, expiry: Date): Promise<void>;
|
|
332
|
+
clear(): Promise<void>;
|
|
333
|
+
}
|
|
334
|
+
declare class InMemoryGeoSessionStorage implements GeoSessionStorage {
|
|
335
|
+
private geoSession;
|
|
336
|
+
private expiry;
|
|
337
|
+
get(): Promise<{
|
|
338
|
+
geoSession: string;
|
|
339
|
+
expiry: Date;
|
|
340
|
+
} | null>;
|
|
341
|
+
set(geoSession: string, expiry: Date): Promise<void>;
|
|
342
|
+
clear(): Promise<void>;
|
|
343
|
+
}
|
|
319
344
|
|
|
320
345
|
declare const DPD_API: {
|
|
321
346
|
readonly BASE_URL: "https://api.dpdlocal.co.uk";
|
|
@@ -408,6 +433,23 @@ declare function testConnection(credentials: DPDCredentials): Promise<{
|
|
|
408
433
|
success: boolean;
|
|
409
434
|
message: string;
|
|
410
435
|
}>;
|
|
436
|
+
declare class GeoSessionManager {
|
|
437
|
+
private credentials;
|
|
438
|
+
private storage;
|
|
439
|
+
private maxRetries;
|
|
440
|
+
private baseRetryDelay;
|
|
441
|
+
constructor(credentials: DPDCredentials, storage?: GeoSessionStorage, options?: {
|
|
442
|
+
maxRetries?: number;
|
|
443
|
+
baseRetryDelay?: number;
|
|
444
|
+
});
|
|
445
|
+
getValidGeoSession(): Promise<string>;
|
|
446
|
+
refreshGeoSession(): Promise<string>;
|
|
447
|
+
clearGeoSession(): Promise<void>;
|
|
448
|
+
private isSessionValid;
|
|
449
|
+
private authenticateWithRetry;
|
|
450
|
+
private authenticate;
|
|
451
|
+
private delay;
|
|
452
|
+
}
|
|
411
453
|
|
|
412
454
|
declare function createShipment(credentials: DPDCredentials, params: CreateShipmentParams, businessConfig: any): Promise<CreateShipmentResult>;
|
|
413
455
|
declare function generateLabel(credentials: DPDCredentials, params: GenerateLabelParams): Promise<GenerateLabelResult>;
|
|
@@ -498,4 +540,4 @@ declare function loggedOperation<T>(params: {
|
|
|
498
540
|
headers?: Record<string, string>;
|
|
499
541
|
}>): Promise<T>;
|
|
500
542
|
|
|
501
|
-
export { type BusinessConfig, type CreateShipmentParams, type CreateShipmentResult, type DPDAddress, type DPDAuthResponse, type DPDConsignment, type DPDContact, type DPDCredentials, type DPDError, type DPDLabelRequest, type DPDLabelResponse, type DPDLogDocument, type DPDModuleConfig, type DPDParcel, _default as DPDService, type DPDServiceCode, type DPDShipmentRequest, type DPDShipmentResponse, DPD_API, type DatabaseAdapter, type GenerateLabelParams, type GenerateLabelResult, type LabelConfig, type LogFilters, type NotificationConfig, type PricingConfig, SERVICE_DESCRIPTIONS, SERVICE_NAMES, type SavedAddress, type ServiceConfig, type ShipmentStatus, type ShipmentStatusUpdate, type ShippingData, type StorageAdapter, type TimestampType, type TrackShipmentParams, type TrackShipmentResult, type ValidateAddressParams, type ValidateAddressResult, authenticate, authenticatedRequest, calculateDPDCost, calculateDeliveryFee, calculateParcels, clearGeoSession, configureLogger, createCompleteShipment, createDPDConfig, createShipment, deleteSavedAddress, generateAndUploadLabel, generateConsignmentRef, generateLabel, getAuthStatus, getEstimatedDeliveryDate, getGeoSession, getLabelUrl, getNextCollectionDate, getSavedAddress, getSavedAddresses, getServiceDescription, getServiceName, getTokenExpiry, getTrackingUrl, hasValidToken, isValidServiceCode, logOperation, loggedOperation, meetsMinimumOrderValue, qualifiesForFreeDelivery, regenerateLabel, saveAddress, setLoggerAdapter, startTimer, testConnection, testDPDConnection, trackShipment, updateSavedAddress, validateAddress, validateDeliveryAddress, validateServiceCode };
|
|
543
|
+
export { type BusinessConfig, type CreateShipmentParams, type CreateShipmentResult, type DPDAddress, type DPDAuthResponse, type DPDConsignment, type DPDContact, type DPDCredentials, type DPDError, type DPDLabelRequest, type DPDLabelResponse, type DPDLogDocument, type DPDModuleConfig, type DPDParcel, _default as DPDService, type DPDServiceCode, type DPDShipmentRequest, type DPDShipmentResponse, DPD_API, type DatabaseAdapter, type GenerateLabelParams, type GenerateLabelResult, GeoSessionManager, type GeoSessionStorage, InMemoryGeoSessionStorage, type LabelConfig, type LogFilters, type NotificationConfig, type PricingConfig, SERVICE_DESCRIPTIONS, SERVICE_NAMES, type SavedAddress, type ServiceConfig, type ShipmentStatus, type ShipmentStatusUpdate, type ShippingData, type StorageAdapter, type TimestampType, type TrackShipmentParams, type TrackShipmentResult, type ValidateAddressParams, type ValidateAddressResult, authenticate, authenticatedRequest, calculateDPDCost, calculateDeliveryFee, calculateParcels, clearGeoSession, configureLogger, createCompleteShipment, createDPDConfig, createShipment, deleteSavedAddress, generateAndUploadLabel, generateConsignmentRef, generateLabel, getAuthStatus, getEstimatedDeliveryDate, getGeoSession, getLabelUrl, getNextCollectionDate, getSavedAddress, getSavedAddresses, getServiceDescription, getServiceName, getTokenExpiry, getTrackingUrl, hasValidToken, isValidServiceCode, logOperation, loggedOperation, meetsMinimumOrderValue, qualifiesForFreeDelivery, regenerateLabel, saveAddress, setLoggerAdapter, startTimer, testConnection, testDPDConnection, trackShipment, updateSavedAddress, validateAddress, validateDeliveryAddress, validateServiceCode };
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,8 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
DPDService: () => dpd_service_default,
|
|
24
24
|
DPD_API: () => DPD_API,
|
|
25
|
+
GeoSessionManager: () => GeoSessionManager,
|
|
26
|
+
InMemoryGeoSessionStorage: () => InMemoryGeoSessionStorage,
|
|
25
27
|
SERVICE_DESCRIPTIONS: () => SERVICE_DESCRIPTIONS,
|
|
26
28
|
SERVICE_NAMES: () => SERVICE_NAMES,
|
|
27
29
|
authenticate: () => authenticate,
|
|
@@ -69,6 +71,28 @@ __export(index_exports, {
|
|
|
69
71
|
});
|
|
70
72
|
module.exports = __toCommonJS(index_exports);
|
|
71
73
|
|
|
74
|
+
// src/types/index.ts
|
|
75
|
+
var InMemoryGeoSessionStorage = class {
|
|
76
|
+
constructor() {
|
|
77
|
+
this.geoSession = null;
|
|
78
|
+
this.expiry = null;
|
|
79
|
+
}
|
|
80
|
+
async get() {
|
|
81
|
+
if (!this.geoSession || !this.expiry) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return { geoSession: this.geoSession, expiry: this.expiry };
|
|
85
|
+
}
|
|
86
|
+
async set(geoSession, expiry) {
|
|
87
|
+
this.geoSession = geoSession;
|
|
88
|
+
this.expiry = expiry;
|
|
89
|
+
}
|
|
90
|
+
async clear() {
|
|
91
|
+
this.geoSession = null;
|
|
92
|
+
this.expiry = null;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
72
96
|
// src/config/index.ts
|
|
73
97
|
var DPD_API = {
|
|
74
98
|
BASE_URL: "https://api.dpdlocal.co.uk",
|
|
@@ -376,9 +400,18 @@ async function authenticatedRequest(credentials, options) {
|
|
|
376
400
|
await delay(DPD_API.RETRY_DELAY * (attempt + 1));
|
|
377
401
|
continue;
|
|
378
402
|
}
|
|
379
|
-
|
|
403
|
+
const err = new Error(
|
|
380
404
|
data?.error?.errorMessage ?? data?.error?.errorAction ?? data?.error?.obj ?? JSON.stringify(data?.error) ?? `Request failed: ${response.statusText}`
|
|
381
405
|
);
|
|
406
|
+
err.name = "DPD_API_ERROR";
|
|
407
|
+
err.dpd = {
|
|
408
|
+
status: response.status,
|
|
409
|
+
statusText: response.statusText,
|
|
410
|
+
error: data?.error,
|
|
411
|
+
raw,
|
|
412
|
+
endpoint: url
|
|
413
|
+
};
|
|
414
|
+
throw err;
|
|
382
415
|
}
|
|
383
416
|
if (hasError && hasData) {
|
|
384
417
|
console.warn(`
|
|
@@ -421,6 +454,164 @@ async function testConnection(credentials) {
|
|
|
421
454
|
};
|
|
422
455
|
}
|
|
423
456
|
}
|
|
457
|
+
var GeoSessionManager = class {
|
|
458
|
+
/**
|
|
459
|
+
* Create a new Geo Session Manager
|
|
460
|
+
*
|
|
461
|
+
* @param credentials - DPD API credentials
|
|
462
|
+
* @param storage - Storage adapter for persistence (optional, defaults to in-memory)
|
|
463
|
+
* @param options - Configuration options
|
|
464
|
+
*/
|
|
465
|
+
constructor(credentials, storage, options) {
|
|
466
|
+
this.credentials = credentials;
|
|
467
|
+
this.storage = storage || new InMemoryGeoSessionStorage();
|
|
468
|
+
this.maxRetries = options?.maxRetries ?? 3;
|
|
469
|
+
this.baseRetryDelay = options?.baseRetryDelay ?? 2e3;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Get a valid geo session token
|
|
473
|
+
*
|
|
474
|
+
* - Loads from persistent storage if available and valid
|
|
475
|
+
* - Automatically refreshes if expired or not found
|
|
476
|
+
* - Retries with exponential backoff on failure
|
|
477
|
+
* - Saves to persistent storage after refresh
|
|
478
|
+
*
|
|
479
|
+
* @returns Valid geo session token
|
|
480
|
+
* @throws Error if authentication fails after retries
|
|
481
|
+
*/
|
|
482
|
+
async getValidGeoSession() {
|
|
483
|
+
try {
|
|
484
|
+
const stored = await this.storage.get();
|
|
485
|
+
if (stored && this.isSessionValid(stored.geoSession, stored.expiry)) {
|
|
486
|
+
console.log("\u2705 Using cached geo session from storage");
|
|
487
|
+
return stored.geoSession;
|
|
488
|
+
}
|
|
489
|
+
console.log("\u{1F504} Fetching new geo session from DPD API...");
|
|
490
|
+
const { geoSession, expiry } = await this.authenticateWithRetry();
|
|
491
|
+
console.log("\u2705 Successfully fetched new geo session");
|
|
492
|
+
await this.storage.set(geoSession, expiry);
|
|
493
|
+
return geoSession;
|
|
494
|
+
} catch (error) {
|
|
495
|
+
console.error("\u274C Error managing geo session:", error);
|
|
496
|
+
throw new Error(
|
|
497
|
+
`Failed to get valid geo session: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Force refresh the geo session
|
|
503
|
+
* Clears cache and fetches a new token
|
|
504
|
+
*
|
|
505
|
+
* @returns New geo session token
|
|
506
|
+
*/
|
|
507
|
+
async refreshGeoSession() {
|
|
508
|
+
await this.storage.clear();
|
|
509
|
+
return this.getValidGeoSession();
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Clear stored geo session
|
|
513
|
+
*/
|
|
514
|
+
async clearGeoSession() {
|
|
515
|
+
await this.storage.clear();
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Check if a session is valid
|
|
519
|
+
*/
|
|
520
|
+
isSessionValid(geoSession, expiry) {
|
|
521
|
+
if (!geoSession || !expiry) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
const now = /* @__PURE__ */ new Date();
|
|
525
|
+
const expiryDate = expiry instanceof Date ? expiry : new Date(expiry);
|
|
526
|
+
if (expiryDate <= now) {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
530
|
+
const expiryStartOfDay = new Date(
|
|
531
|
+
expiryDate.getFullYear(),
|
|
532
|
+
expiryDate.getMonth(),
|
|
533
|
+
expiryDate.getDate()
|
|
534
|
+
);
|
|
535
|
+
if (expiryStartOfDay.getTime() === startOfToday.getTime()) {
|
|
536
|
+
const sessionCreatedAt = new Date(expiryDate.getTime() - 90 * 60 * 1e3);
|
|
537
|
+
const hoursSinceCreation = (now.getTime() - sessionCreatedAt.getTime()) / (1e3 * 60 * 60);
|
|
538
|
+
if (hoursSinceCreation > 12) {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Authenticate with retry logic and exponential backoff
|
|
546
|
+
*/
|
|
547
|
+
async authenticateWithRetry() {
|
|
548
|
+
let lastError = null;
|
|
549
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
550
|
+
try {
|
|
551
|
+
console.log(`\u{1F4E1} Attempt ${attempt}/${this.maxRetries} to fetch geo session...`);
|
|
552
|
+
const geoSession = await this.authenticate();
|
|
553
|
+
const expiry = new Date(Date.now() + 90 * 60 * 1e3);
|
|
554
|
+
return { geoSession, expiry };
|
|
555
|
+
} catch (error) {
|
|
556
|
+
lastError = error;
|
|
557
|
+
console.error(`\u274C Attempt ${attempt}/${this.maxRetries} failed:`, error);
|
|
558
|
+
if (attempt < this.maxRetries) {
|
|
559
|
+
const delay2 = this.baseRetryDelay * Math.pow(2, attempt - 1);
|
|
560
|
+
console.log(`\u23F3 Retrying in ${delay2}ms...`);
|
|
561
|
+
await this.delay(delay2);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
throw new Error(
|
|
566
|
+
`Failed to authenticate after ${this.maxRetries} attempts: ${lastError?.message || "Unknown error"}`
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Authenticate with DPD API
|
|
571
|
+
*/
|
|
572
|
+
async authenticate() {
|
|
573
|
+
const { username, password } = this.credentials;
|
|
574
|
+
const authHeader = Buffer.from(`${username}:${password}`).toString("base64");
|
|
575
|
+
const response = await fetch(`${DPD_API.BASE_URL}${DPD_API.ENDPOINTS.AUTH}`, {
|
|
576
|
+
method: "POST",
|
|
577
|
+
headers: {
|
|
578
|
+
"Content-Type": "application/json",
|
|
579
|
+
Accept: "application/json",
|
|
580
|
+
Authorization: `Basic ${authHeader}`
|
|
581
|
+
},
|
|
582
|
+
signal: AbortSignal.timeout(DPD_API.TIMEOUT)
|
|
583
|
+
});
|
|
584
|
+
const raw = await response.text();
|
|
585
|
+
let payload;
|
|
586
|
+
try {
|
|
587
|
+
payload = raw ? JSON.parse(raw) : null;
|
|
588
|
+
} catch {
|
|
589
|
+
if (!response.ok) {
|
|
590
|
+
throw new Error(`Authentication failed: ${raw || response.statusText}`);
|
|
591
|
+
}
|
|
592
|
+
throw new Error("Invalid JSON response from DPD API");
|
|
593
|
+
}
|
|
594
|
+
if (!response.ok) {
|
|
595
|
+
throw new Error(
|
|
596
|
+
payload?.error?.errorMessage ?? `Authentication failed: ${response.status} ${response.statusText}`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
if (payload?.error) {
|
|
600
|
+
throw new Error(payload.error.errorMessage ?? "Authentication failed");
|
|
601
|
+
}
|
|
602
|
+
const geoSession = payload?.data?.geoSession;
|
|
603
|
+
if (!geoSession) {
|
|
604
|
+
throw new Error("No GeoSession token received from DPD");
|
|
605
|
+
}
|
|
606
|
+
return geoSession;
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Delay helper for retry backoff
|
|
610
|
+
*/
|
|
611
|
+
delay(ms) {
|
|
612
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
613
|
+
}
|
|
614
|
+
};
|
|
424
615
|
|
|
425
616
|
// src/utils/getAcceptHeader.ts
|
|
426
617
|
function getAcceptHeader(format) {
|
|
@@ -540,10 +731,12 @@ async function createShipment(credentials, params, businessConfig) {
|
|
|
540
731
|
trackingUrl
|
|
541
732
|
};
|
|
542
733
|
} catch (error) {
|
|
734
|
+
const errorDetails = error instanceof Error ? error.dpd : void 0;
|
|
543
735
|
return {
|
|
544
736
|
success: false,
|
|
545
737
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
546
|
-
errorCode: "SHIPMENT_CREATION_FAILED"
|
|
738
|
+
errorCode: "SHIPMENT_CREATION_FAILED",
|
|
739
|
+
errorDetails
|
|
547
740
|
};
|
|
548
741
|
}
|
|
549
742
|
}
|
|
@@ -1073,6 +1266,8 @@ function logToConsole(logData) {
|
|
|
1073
1266
|
0 && (module.exports = {
|
|
1074
1267
|
DPDService,
|
|
1075
1268
|
DPD_API,
|
|
1269
|
+
GeoSessionManager,
|
|
1270
|
+
InMemoryGeoSessionStorage,
|
|
1076
1271
|
SERVICE_DESCRIPTIONS,
|
|
1077
1272
|
SERVICE_NAMES,
|
|
1078
1273
|
authenticate,
|
|
@@ -1125,7 +1320,7 @@ function logToConsole(logData) {
|
|
|
1125
1320
|
* Database-agnostic and framework-independent
|
|
1126
1321
|
*
|
|
1127
1322
|
* @package @jazzdev/dpd-local-sdk
|
|
1128
|
-
* @version 1.0
|
|
1323
|
+
* @version 1.1.0
|
|
1129
1324
|
* @author Taiow Babarinde <babsman4all@gmail.com>
|
|
1130
1325
|
* @license MIT
|
|
1131
1326
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
// src/types/index.ts
|
|
2
|
+
var InMemoryGeoSessionStorage = class {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.geoSession = null;
|
|
5
|
+
this.expiry = null;
|
|
6
|
+
}
|
|
7
|
+
async get() {
|
|
8
|
+
if (!this.geoSession || !this.expiry) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return { geoSession: this.geoSession, expiry: this.expiry };
|
|
12
|
+
}
|
|
13
|
+
async set(geoSession, expiry) {
|
|
14
|
+
this.geoSession = geoSession;
|
|
15
|
+
this.expiry = expiry;
|
|
16
|
+
}
|
|
17
|
+
async clear() {
|
|
18
|
+
this.geoSession = null;
|
|
19
|
+
this.expiry = null;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
1
23
|
// src/config/index.ts
|
|
2
24
|
var DPD_API = {
|
|
3
25
|
BASE_URL: "https://api.dpdlocal.co.uk",
|
|
@@ -305,9 +327,18 @@ async function authenticatedRequest(credentials, options) {
|
|
|
305
327
|
await delay(DPD_API.RETRY_DELAY * (attempt + 1));
|
|
306
328
|
continue;
|
|
307
329
|
}
|
|
308
|
-
|
|
330
|
+
const err = new Error(
|
|
309
331
|
data?.error?.errorMessage ?? data?.error?.errorAction ?? data?.error?.obj ?? JSON.stringify(data?.error) ?? `Request failed: ${response.statusText}`
|
|
310
332
|
);
|
|
333
|
+
err.name = "DPD_API_ERROR";
|
|
334
|
+
err.dpd = {
|
|
335
|
+
status: response.status,
|
|
336
|
+
statusText: response.statusText,
|
|
337
|
+
error: data?.error,
|
|
338
|
+
raw,
|
|
339
|
+
endpoint: url
|
|
340
|
+
};
|
|
341
|
+
throw err;
|
|
311
342
|
}
|
|
312
343
|
if (hasError && hasData) {
|
|
313
344
|
console.warn(`
|
|
@@ -350,6 +381,164 @@ async function testConnection(credentials) {
|
|
|
350
381
|
};
|
|
351
382
|
}
|
|
352
383
|
}
|
|
384
|
+
var GeoSessionManager = class {
|
|
385
|
+
/**
|
|
386
|
+
* Create a new Geo Session Manager
|
|
387
|
+
*
|
|
388
|
+
* @param credentials - DPD API credentials
|
|
389
|
+
* @param storage - Storage adapter for persistence (optional, defaults to in-memory)
|
|
390
|
+
* @param options - Configuration options
|
|
391
|
+
*/
|
|
392
|
+
constructor(credentials, storage, options) {
|
|
393
|
+
this.credentials = credentials;
|
|
394
|
+
this.storage = storage || new InMemoryGeoSessionStorage();
|
|
395
|
+
this.maxRetries = options?.maxRetries ?? 3;
|
|
396
|
+
this.baseRetryDelay = options?.baseRetryDelay ?? 2e3;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get a valid geo session token
|
|
400
|
+
*
|
|
401
|
+
* - Loads from persistent storage if available and valid
|
|
402
|
+
* - Automatically refreshes if expired or not found
|
|
403
|
+
* - Retries with exponential backoff on failure
|
|
404
|
+
* - Saves to persistent storage after refresh
|
|
405
|
+
*
|
|
406
|
+
* @returns Valid geo session token
|
|
407
|
+
* @throws Error if authentication fails after retries
|
|
408
|
+
*/
|
|
409
|
+
async getValidGeoSession() {
|
|
410
|
+
try {
|
|
411
|
+
const stored = await this.storage.get();
|
|
412
|
+
if (stored && this.isSessionValid(stored.geoSession, stored.expiry)) {
|
|
413
|
+
console.log("\u2705 Using cached geo session from storage");
|
|
414
|
+
return stored.geoSession;
|
|
415
|
+
}
|
|
416
|
+
console.log("\u{1F504} Fetching new geo session from DPD API...");
|
|
417
|
+
const { geoSession, expiry } = await this.authenticateWithRetry();
|
|
418
|
+
console.log("\u2705 Successfully fetched new geo session");
|
|
419
|
+
await this.storage.set(geoSession, expiry);
|
|
420
|
+
return geoSession;
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error("\u274C Error managing geo session:", error);
|
|
423
|
+
throw new Error(
|
|
424
|
+
`Failed to get valid geo session: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Force refresh the geo session
|
|
430
|
+
* Clears cache and fetches a new token
|
|
431
|
+
*
|
|
432
|
+
* @returns New geo session token
|
|
433
|
+
*/
|
|
434
|
+
async refreshGeoSession() {
|
|
435
|
+
await this.storage.clear();
|
|
436
|
+
return this.getValidGeoSession();
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Clear stored geo session
|
|
440
|
+
*/
|
|
441
|
+
async clearGeoSession() {
|
|
442
|
+
await this.storage.clear();
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Check if a session is valid
|
|
446
|
+
*/
|
|
447
|
+
isSessionValid(geoSession, expiry) {
|
|
448
|
+
if (!geoSession || !expiry) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
const now = /* @__PURE__ */ new Date();
|
|
452
|
+
const expiryDate = expiry instanceof Date ? expiry : new Date(expiry);
|
|
453
|
+
if (expiryDate <= now) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
457
|
+
const expiryStartOfDay = new Date(
|
|
458
|
+
expiryDate.getFullYear(),
|
|
459
|
+
expiryDate.getMonth(),
|
|
460
|
+
expiryDate.getDate()
|
|
461
|
+
);
|
|
462
|
+
if (expiryStartOfDay.getTime() === startOfToday.getTime()) {
|
|
463
|
+
const sessionCreatedAt = new Date(expiryDate.getTime() - 90 * 60 * 1e3);
|
|
464
|
+
const hoursSinceCreation = (now.getTime() - sessionCreatedAt.getTime()) / (1e3 * 60 * 60);
|
|
465
|
+
if (hoursSinceCreation > 12) {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Authenticate with retry logic and exponential backoff
|
|
473
|
+
*/
|
|
474
|
+
async authenticateWithRetry() {
|
|
475
|
+
let lastError = null;
|
|
476
|
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
|
477
|
+
try {
|
|
478
|
+
console.log(`\u{1F4E1} Attempt ${attempt}/${this.maxRetries} to fetch geo session...`);
|
|
479
|
+
const geoSession = await this.authenticate();
|
|
480
|
+
const expiry = new Date(Date.now() + 90 * 60 * 1e3);
|
|
481
|
+
return { geoSession, expiry };
|
|
482
|
+
} catch (error) {
|
|
483
|
+
lastError = error;
|
|
484
|
+
console.error(`\u274C Attempt ${attempt}/${this.maxRetries} failed:`, error);
|
|
485
|
+
if (attempt < this.maxRetries) {
|
|
486
|
+
const delay2 = this.baseRetryDelay * Math.pow(2, attempt - 1);
|
|
487
|
+
console.log(`\u23F3 Retrying in ${delay2}ms...`);
|
|
488
|
+
await this.delay(delay2);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
throw new Error(
|
|
493
|
+
`Failed to authenticate after ${this.maxRetries} attempts: ${lastError?.message || "Unknown error"}`
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Authenticate with DPD API
|
|
498
|
+
*/
|
|
499
|
+
async authenticate() {
|
|
500
|
+
const { username, password } = this.credentials;
|
|
501
|
+
const authHeader = Buffer.from(`${username}:${password}`).toString("base64");
|
|
502
|
+
const response = await fetch(`${DPD_API.BASE_URL}${DPD_API.ENDPOINTS.AUTH}`, {
|
|
503
|
+
method: "POST",
|
|
504
|
+
headers: {
|
|
505
|
+
"Content-Type": "application/json",
|
|
506
|
+
Accept: "application/json",
|
|
507
|
+
Authorization: `Basic ${authHeader}`
|
|
508
|
+
},
|
|
509
|
+
signal: AbortSignal.timeout(DPD_API.TIMEOUT)
|
|
510
|
+
});
|
|
511
|
+
const raw = await response.text();
|
|
512
|
+
let payload;
|
|
513
|
+
try {
|
|
514
|
+
payload = raw ? JSON.parse(raw) : null;
|
|
515
|
+
} catch {
|
|
516
|
+
if (!response.ok) {
|
|
517
|
+
throw new Error(`Authentication failed: ${raw || response.statusText}`);
|
|
518
|
+
}
|
|
519
|
+
throw new Error("Invalid JSON response from DPD API");
|
|
520
|
+
}
|
|
521
|
+
if (!response.ok) {
|
|
522
|
+
throw new Error(
|
|
523
|
+
payload?.error?.errorMessage ?? `Authentication failed: ${response.status} ${response.statusText}`
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
if (payload?.error) {
|
|
527
|
+
throw new Error(payload.error.errorMessage ?? "Authentication failed");
|
|
528
|
+
}
|
|
529
|
+
const geoSession = payload?.data?.geoSession;
|
|
530
|
+
if (!geoSession) {
|
|
531
|
+
throw new Error("No GeoSession token received from DPD");
|
|
532
|
+
}
|
|
533
|
+
return geoSession;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Delay helper for retry backoff
|
|
537
|
+
*/
|
|
538
|
+
delay(ms) {
|
|
539
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
540
|
+
}
|
|
541
|
+
};
|
|
353
542
|
|
|
354
543
|
// src/utils/getAcceptHeader.ts
|
|
355
544
|
function getAcceptHeader(format) {
|
|
@@ -469,10 +658,12 @@ async function createShipment(credentials, params, businessConfig) {
|
|
|
469
658
|
trackingUrl
|
|
470
659
|
};
|
|
471
660
|
} catch (error) {
|
|
661
|
+
const errorDetails = error instanceof Error ? error.dpd : void 0;
|
|
472
662
|
return {
|
|
473
663
|
success: false,
|
|
474
664
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
475
|
-
errorCode: "SHIPMENT_CREATION_FAILED"
|
|
665
|
+
errorCode: "SHIPMENT_CREATION_FAILED",
|
|
666
|
+
errorDetails
|
|
476
667
|
};
|
|
477
668
|
}
|
|
478
669
|
}
|
|
@@ -1001,6 +1192,8 @@ function logToConsole(logData) {
|
|
|
1001
1192
|
export {
|
|
1002
1193
|
dpd_service_default as DPDService,
|
|
1003
1194
|
DPD_API,
|
|
1195
|
+
GeoSessionManager,
|
|
1196
|
+
InMemoryGeoSessionStorage,
|
|
1004
1197
|
SERVICE_DESCRIPTIONS,
|
|
1005
1198
|
SERVICE_NAMES,
|
|
1006
1199
|
authenticate,
|
|
@@ -1053,7 +1246,7 @@ export {
|
|
|
1053
1246
|
* Database-agnostic and framework-independent
|
|
1054
1247
|
*
|
|
1055
1248
|
* @package @jazzdev/dpd-local-sdk
|
|
1056
|
-
* @version 1.0
|
|
1249
|
+
* @version 1.1.0
|
|
1057
1250
|
* @author Taiow Babarinde <babsman4all@gmail.com>
|
|
1058
1251
|
* @license MIT
|
|
1059
1252
|
*/
|
package/package.json
CHANGED