@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 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
- throw new Error(
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.12
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
- throw new Error(
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.12
1249
+ * @version 1.1.0
1057
1250
  * @author Taiow Babarinde <babsman4all@gmail.com>
1058
1251
  * @license MIT
1059
1252
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jazzdev/dpd-local-sdk",
3
- "version": "1.0.13",
3
+ "version": "1.1.1",
4
4
  "description": "TypeScript SDK for DPD Local shipping API integration - database-agnostic and framework-independent",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",