weshipyou-sdk 1.0.0
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/LICENSE +33 -0
- package/README.md +49 -0
- package/dist/chunk-KT3VLIZI.mjs +18 -0
- package/dist/chunk-KT3VLIZI.mjs.map +1 -0
- package/dist/chunk-RM3JFTCX.cjs +18 -0
- package/dist/chunk-RM3JFTCX.cjs.map +1 -0
- package/dist/cli.js +20 -0
- package/dist/cli.js.map +1 -0
- package/dist/enterprise.cjs +73 -0
- package/dist/enterprise.cjs.map +1 -0
- package/dist/enterprise.d.mts +398 -0
- package/dist/enterprise.d.ts +398 -0
- package/dist/enterprise.mjs +73 -0
- package/dist/enterprise.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +221 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/update-shipment.use-case-BsHiVqQV.d.mts +504 -0
- package/dist/update-shipment.use-case-BsHiVqQV.d.ts +504 -0
- package/package.json +106 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkRM3JFTCXcjs = require('./chunk-RM3JFTCX.cjs');function ve(o,e,t=3e4){return async()=>{let r=setTimeout(()=>process.exit(1),t);o.close(),await Promise.all(e.map(n=>n.close().catch(()=>{}))),clearTimeout(r),process.exit(0)}}var x=class{constructor(){this.token=null;this.exp=0}setToken(e,t){this.token=e,this.exp=t}getToken(){return this.token}isExpired(){return this.exp?this.exp*1e3<Date.now():!0}clear(){this.token=null,this.exp=0}};var _axios = require('axios'); var _axios2 = _interopRequireDefault(_axios);var y=class extends Error{constructor(t,r,n){super(r);this.statusCode=t;this.details=n;this.name="ApiError"}},F=class extends y{constructor(e="Authentication failed"){super(401,e)}},k=class extends y{constructor(e="Rate limit exceeded"){super(429,e)}};var _crypto = require('crypto');var _pino = require('pino'); var _pino2 = _interopRequireDefault(_pino);var ke=_pino2.default.call(void 0, {level:"info"}),I= exports.AxiosHttpClient =class{constructor(e,t,r,n=3,i=1e3,m=[/add-funds/i,/mobile-recharges/i,/recharges/i],p){this.refreshPromise=null;this.lastRateLimit=null;this.client=_axios2.default.create({baseURL:e,timeout:1e4}),this.tokenManager=t,this.refreshTokenFn=r,this.retries=n,this.baseDelayMs=i,this.idempotencyPaths=m,this.onRateLimit=p,this.client.interceptors.request.use(s=>{let a=this.tokenManager.getToken();return a&&!s.headers.Authorization&&(s.headers.Authorization=`Bearer ${a}`),s}),this.client.interceptors.response.use(s=>s,async s=>{let a=s.config;if(_optionalChain([s, 'access', _2 => _2.response, 'optionalAccess', _3 => _3.status])===401&&!a._retry&&(a._retry=!0,this.refreshTokenFn))try{this.refreshPromise||(this.refreshPromise=this.refreshTokenFn());let{accessToken:c,exp:l}=await this.refreshPromise;return this.refreshPromise=null,this.tokenManager.setToken(c,l),a.headers.Authorization=`Bearer ${c}`,this.client(a)}catch (e2){throw this.refreshPromise=null,ke.warn("Token refresh failed"),this.tokenManager.clear(),new F("Session expired")}if(s.response){let{status:c,data:l}=s.response;if(c===429)throw new k(_optionalChain([l, 'optionalAccess', _4 => _4.message]));if(c>=400)throw new y(c,_optionalChain([l, 'optionalAccess', _5 => _5.message])||"API request failed",l)}throw s})}getRateLimitInfo(){return this.lastRateLimit}async request(e){let t=0;for(;t<this.retries;)try{let r={method:e.method.toLowerCase(),url:e.url,data:e.body,headers:{...e.headers}};e.method==="POST"&&this.idempotencyPaths.some(a=>a.test(e.url))&&(r.headers={...r.headers||{},"Idempotency-Key":_crypto.randomUUID.call(void 0, )});let n=await this.client.request(r),i=n.headers["x-ratelimit-remaining"],m=n.headers["x-ratelimit-limit"],p=n.headers["x-ratelimit-reset"];return i!==void 0&&m!==void 0&&p!==void 0&&(this.lastRateLimit={remaining:Number(i),limit:Number(m),reset:Number(p)},_optionalChain([this, 'access', _6 => _6.onRateLimit, 'optionalCall', _7 => _7(this.lastRateLimit)])),{status:n.status,data:n.data,headers:n.headers}}catch(r){t++;let n=r instanceof k||_optionalChain([r, 'optionalAccess', _8 => _8.response, 'optionalAccess', _9 => _9.status])>=500||_optionalChain([r, 'optionalAccess', _10 => _10.statusCode])>=500;if(t>=this.retries||!n)throw r;await new Promise(i=>setTimeout(i,this.baseDelayMs*Math.pow(2,t-1)))}throw new Error("Max retries exceeded")}};var R=class{constructor(e,t){this.httpClient=e;this.tokenManager=t}async authenticate(e,t,r="en-US"){let i=(await this.httpClient.request({method:"POST",url:"/api/v1/authentication",body:{username:e,password:t},headers:{"Accept-Language":r}})).data;return this.tokenManager&&i.accessToken&&this.tokenManager.setToken(i.accessToken,i.exp||0),i}};var T=class{constructor(e){this.httpClient=e}async addSubAccount(e,t="en-US"){return(await this.httpClient.request({method:"POST",url:"/api/v1/add-sub-account",body:e,headers:{"Accept-Language":t}})).data}async addFunds(e,t="en-US"){return(await this.httpClient.request({method:"POST",url:"/api/v1/add-funds",body:{paymentMethod:"zelle",...e},headers:{"Accept-Language":t}})).data}};var P=class{constructor(e){this.httpClient=e}async createRecharge(e,t="en-US"){return(await this.httpClient.request({method:"POST",url:"/api/v1/mobile-recharges/recharges",body:e,headers:{"Accept-Language":t}})).data}async getRecharges(e=20,t="en-US"){return(await this.httpClient.request({method:"GET",url:`/api/v1/mobile-recharges/recharges?limit=${encodeURIComponent(String(e))}`,headers:{"Accept-Language":t}})).data}};var A=class{async pollUntil(e,t,r={}){let{maxAttempts:n=5,baseDelayMs:i=1e3,abortSignal:m}=r,p=0;for(;p<n;){if(_optionalChain([m, 'optionalAccess', _11 => _11.aborted]))throw new DOMException("Polling aborted","AbortError");let s=await e();if(t(s))return s;if(p++,p>=n)throw new Error(`Polling limit reached (${n})`);let a=i*Math.pow(2,p-1);await new Promise(c=>setTimeout(c,a))}throw new Error("Unreachable polling path")}};var C=class{constructor(e,t){this.authService=e;this.shipmentService=t}async execute(e,t,r,n="en-US"){return await this.authService.authenticate(e,t,n),this.shipmentService.getRates(r,n)}};var E=class{constructor(e,t){this.authService=e;this.shipmentService=t}async execute(e,t,r,n="en-US"){return await this.authService.authenticate(e,t,n),this.shipmentService.createShipment(r,n)}};var _zod = require('zod');var ie=_zod.z.object({accountUid:_zod.z.string().optional(),externalAccountNumber:_zod.z.string().optional(),name:_zod.z.string().min(1),displayName:_zod.z.string().min(1),systemUnits:_zod.z.enum(["imperial","metric"]),email:_zod.z.string().email(),notificationEmail:_zod.z.string().email(),website:_zod.z.string().url().optional().or(_zod.z.literal("")),phone:_zod.z.string().min(6),emergencyPhone:_zod.z.string().min(6),taxId:_zod.z.string().optional(),taxIdCountry:_zod.z.object({isoCode:_zod.z.string().length(2)}).optional(),isBusiness:_zod.z.boolean().default(!1),allowBankAccountPayments:_zod.z.boolean().default(!1),address:_zod.z.object({street:_zod.z.string(),city:_zod.z.string(),state:_zod.z.string(),postalCode:_zod.z.string(),countryIsoCode:_zod.z.string().length(2)}).optional()});var v=class extends Error{constructor(t){super("Validation failed");this.details=t;this.name="ValidationError"}};var O=class{constructor(e,t){this.authService=e;this.accountService=t}async execute(e,t,r,n="en-US"){let i=ie.safeParse(r);if(!i.success)throw new v(i.error.flatten().fieldErrors);return await this.authService.authenticate(e,t,n),this.accountService.addSubAccount(i.data,n)}};var oe=_zod.z.object({paymentMethod:_zod.z.enum(["zelle","credit_card","balance"]).default("zelle"),accountUid:_zod.z.string().optional(),rechargeable_product:_zod.z.object({id:_zod.z.number().int().positive()}),scheduleDate:_zod.z.string().datetime().optional().nullable(),amount:_zod.z.number().positive(),account_rechargeable_contact:_zod.z.object({name:_zod.z.string().min(1),accountNumber:_zod.z.string().min(1)})});var M=class{constructor(e,t){this.authService=e;this.rechargeService=t}async execute(e,t,r,n="en-US"){let i=oe.safeParse(r);if(!i.success)throw new v(i.error.flatten().fieldErrors);return await this.authService.authenticate(e,t,n),this.rechargeService.createRecharge(i.data,n)}};var q=class{constructor(e,t,r){this.httpClient=e;this.pollingService=t;this.circuitBreaker=r}async execute(e,t,r,n="en-US"){let i=async()=>await this.circuitBreaker.execute(async()=>(await this.httpClient.request({method:"GET",url:`/api/v1/shipments/${e}`,headers:{"Accept-Language":n}})).data);return await this.pollingService.pollUntil(i,m=>m.status===t,{maxAttempts:8,baseDelayMs:2e3,abortSignal:r})}};var H=class{constructor(e,t="sha256"){this.secret=e;this.algorithm=t}verifySignature(e,t,r,n){let i=r||this.secret,m=n||this.algorithm,p=_crypto.createHmac.call(void 0, m,i).update(e).digest("hex");if(p.length!==t.length)return!1;try{return _crypto.timingSafeEqual.call(void 0, Buffer.from(p),Buffer.from(t))}catch (e3){return!1}}async handle(e){console.log(`[Webhook] Received ${e.eventType} at ${e.timestamp}`)}};var L=class{constructor(e=5,t=3e4){this.state="CLOSED";this.failures=0;this.lastFailureTime=null;this.failureThreshold=e,this.resetTimeoutMs=t}async execute(e){if(this.state==="OPEN"){let t=Date.now();if(this.lastFailureTime&&t-this.lastFailureTime>this.resetTimeoutMs)this.state="HALF-OPEN";else throw new Error("Circuit breaker is OPEN")}try{let t=await e();return this.onSuccess(),t}catch(t){throw this.onFailure(),t}}getState(){return this.state}reset(){this.state="CLOSED",this.failures=0,this.lastFailureTime=null}onSuccess(){this.failures=0,this.state==="HALF-OPEN"&&(this.state="CLOSED")}onFailure(){this.failures++,this.lastFailureTime=Date.now(),this.failures>=this.failureThreshold&&(this.state="OPEN")}};var _expressratelimit = require('express-rate-limit'); var _expressratelimit2 = _interopRequireDefault(_expressratelimit);var N=class{constructor(e={}){this.limiter=_expressratelimit2.default.call(void 0, {windowMs:900*1e3,max:100,standardHeaders:!0,legacyHeaders:!1,...e})}async consume(e,t=1){return Promise.resolve({remaining:99,limit:100})}getMiddleware(){return(e,t,r)=>this.limiter(e,t,r)}async close(){}};var _api = require('@opentelemetry/api');var _sdknode = require('@opentelemetry/sdk-node');var _autoinstrumentationsnode = require('@opentelemetry/auto-instrumentations-node');var _exportertraceotlphttp = require('@opentelemetry/exporter-trace-otlp-http');var f=null;function V(o="weshipyou-sdk"){return f||(f=new (0, _sdknode.NodeSDK)({serviceName:o,traceExporter:new (0, _exportertraceotlphttp.OTLPTraceExporter)({url:process.env.OTEL_ENDPOINT||"http://localhost:4318/v1/traces"}),instrumentations:[_autoinstrumentationsnode.getNodeAutoInstrumentations.call(void 0, )]}),f.start(),f)}async function J(){f&&(await f.shutdown(),f=null)}var D=class{constructor(){this.tracer=_api.trace.getTracer("weshipyou-sdk")}startSpan(e,t){let r=this.tracer.startSpan(e);return t&&Object.entries(t).forEach(([n,i])=>r.setAttribute(n,i)),{setAttribute:(n,i)=>r.setAttribute(n,i),end:()=>r.end()}}async close(){await J()}};var _express = require('express');function se(o,e,t,r){let n=_express.Router.call(void 0, );return n.use(o.getMiddleware()),n.use(r),n.post("/shipments/rates",async(i,m,p)=>{let s=e.startSpan("POST /shipments/rates");s.setAttribute("account.id",_optionalChain([i, 'access', _12 => _12.body, 'optionalAccess', _13 => _13.accountId])||"unknown");try{let a=await t.rates(i.body);m.json(a)}catch(a){p(a)}finally{s.end()}}),n.post("/shipments",async(i,m,p)=>{let s=e.startSpan("POST /shipments");s.setAttribute("account.id",_optionalChain([i, 'access', _14 => _14.body, 'optionalAccess', _15 => _15.accountId])||"unknown");try{let a=await t.createShipment(i.body);m.status(201).json(a)}catch(a){p(a)}finally{s.end()}}),n.get("/shipments/:id",async(i,m,p)=>{let s=e.startSpan("GET /shipments/:id");s.setAttribute("shipment.id",i.params.id);let a=new AbortController;i.on("close",()=>a.abort());try{let c=i.query.targetStatus||"delivered",l=await t.trackShipment(i.params.id,c);m.json(l)}catch(c){p(c)}finally{s.end()}}),n.post("/webhooks",async(i,m,p)=>{let s=e.startSpan("POST /webhooks"),a=i.headers["x-websignature"],c=Array.isArray(a)?a[0]:a;s.setAttribute("webhook.signature",c||"none");try{if(!c)return m.status(401).json({error:"Missing signature header"});await t.webhook(c,JSON.stringify(i.body)),m.status(200).json({received:!0})}catch(l){p(l)}finally{s.end()}}),n}function ae(o){return(e,t,r)=>{let n=e.headers.authorization;if(!n||!n.startsWith("Bearer ")){t.status(401).json({error:"Missing or invalid Authorization header"});return}let i=n.slice(7);if(!i||i.length<10){t.status(401).json({error:"Invalid token format"});return}r()}}var U=class{constructor(){this.totalRequests=0;this.failedRequests=0;this.circuitBreakerTrips=0;this.responseTimes=[];this.maxSamples=1e3}recordRequest(e,t){this.totalRequests++,this.responseTimes.push(e),this.responseTimes.length>this.maxSamples&&this.responseTimes.shift(),t||this.failedRequests++}recordCircuitBreakerTrip(){this.circuitBreakerTrips++}snapshot(){let e=this.responseTimes.length?this.responseTimes.reduce((t,r)=>t+r,0)/this.responseTimes.length:0;return{totalRequests:this.totalRequests,failedRequests:this.failedRequests,circuitBreakerTrips:this.circuitBreakerTrips,averageResponseTimeMs:Math.round(e)}}reset(){this.totalRequests=0,this.failedRequests=0,this.circuitBreakerTrips=0,this.responseTimes=[]}};function ce(o,e){let t=[],r;return o.includes("sandbox")||o.includes("staging")||o.includes("test")?r="test":(o.includes("weshipyou.com/api/v1"),r="production"),e&&(e.startsWith("test_")&&r==="production"&&t.push('API key starts with "test_" but environment is production'),e.startsWith("live_")&&r==="test"&&t.push('API key starts with "live_" but environment is test')),{environment:r,warnings:t}}var me=_zod.z.object({eventType:_zod.z.string().min(1),payload:_zod.z.record(_zod.z.unknown()).default({}),timestamp:_zod.z.string().datetime().optional()});function Me(o,e,t){let{warnings:r}=ce(o);r.forEach(d=>console.warn(d)),t&&(process.env.OTEL_ENDPOINT=t),process.env.OTEL_ENDPOINT&&V();let n=new x,i=new L(5,3e4),m=new A,p=new U,s=new N,a=new D,c=new I(o,n,async()=>{throw new Error("Auto-refresh credentials not configured")}),l=new R(c,n),ee=new (0, _chunkRM3JFTCXcjs.a)(c),ue=new T(c),pe=new P(c),W,j;if(typeof e=="string")W=e,j=void 0;else if(e)W=e.secret,j=e.algorithm;else throw new Error("Webhook secret is required");let z=new H(W,j),te=new C(l,ee),re=new E(l,ee),le=new O(l,ue),he=new M(l,pe),ne=new q(c,m,i),de=se(s,a,{rates:d=>{let g=d,{username:b,password:w,...G}=g;return te.execute(b,w,G)},createShipment:d=>{let g=d,{username:b,password:w,...G}=g;return re.execute(b,w,G)},trackShipment:(d,g)=>ne.execute(d,g),webhook:async(d,g)=>{if(!z.verifySignature(g,d))throw new Error("Invalid webhook signature");let b=JSON.parse(g),w=me.parse(b);await z.handle(w)}},ae({tokenManager:n}));return{auth:l,httpClient:c,getRates:te,createShipment:re,createSubAccount:le,executeRecharge:he,trackShipment:ne,webhookHandler:z,pollingService:m,circuitBreaker:i,metricsCollector:p,rateLimiter:s,tracer:a,router:de}}var _opossum = require('opossum'); var _opossum2 = _interopRequireDefault(_opossum);function He(o){return new (0, _opossum2.default)(t=>Promise.resolve(t()),o)}var K=class{constructor(e={}){this.breaker=He({timeout:1e4,errorThresholdPercentage:50,resetTimeout:3e4,...e})}async execute(e){return await this.breaker.fire(e)}getState(){let e=this.breaker.stats,t=e.failures+e.successes;return t===0?"CLOSED":e.failures/t*100>=50?"OPEN":"CLOSED"}reset(){this.breaker.reset()}getMetrics(){let e=this.breaker.stats;return{success:e.successes,failure:e.failures,timeout:e.timeouts,rejection:e.rejects}}};var _helmet = require('helmet'); var _helmet2 = _interopRequireDefault(_helmet);var Q=class{getHandler(){return _helmet2.default.call(void 0, {contentSecurityPolicy:{directives:{defaultSrc:["'self'"],scriptSrc:["'self'"],styleSrc:["'self'","'unsafe-inline'"],imgSrc:["'self'","data:","https:"],connectSrc:["'self'","https://weshipyou.com"],fontSrc:["'self'"],objectSrc:["'none'"],upgradeInsecureRequests:[]}},crossOriginEmbedderPolicy:!0,crossOriginOpenerPolicy:!0,crossOriginResourcePolicy:{policy:"same-site"},originAgentCluster:!0,referrerPolicy:{policy:"strict-origin-when-cross-origin"},strictTransportSecurity:{maxAge:31536e3,includeSubDomains:!0},xContentTypeOptions:!0,xDnsPrefetchControl:!0,xDownloadOptions:!0,xFrameOptions:{action:"sameorigin"},xPermittedCrossDomainPolicies:{permittedPolicies:"none"},xXssProtection:!0})}};var X=class{constructor(e,t,r,n=new Date().toISOString(),i=1){this.eventId=e;this.aggregateId=t;this.payload=r;this.timestamp=n;this.version=i;this.type="webhook.received"}},Y= exports.WebhookProcessedEvent =class{constructor(e,t,r,n=new Date().toISOString(),i=1){this.eventId=e;this.aggregateId=t;this.payload=r;this.timestamp=n;this.version=i;this.type="webhook.processed"}};var Z=class{constructor(e,t){this.eventStore=e;this.httpClient=t}async onShipmentCreated(e){await this.eventStore.append(e.aggregateId,[e])}async onShipmentStatusChanged(e){await this.eventStore.append(e.aggregateId,[new (0, _chunkRM3JFTCXcjs.e)(crypto.randomUUID(),e.aggregateId,{...e.payload},new Date().toISOString(),e.version+1)])}async onWebhookReceived(e){if(e.type==="shipment.status_changed"){let t=new (0, _chunkRM3JFTCXcjs.e)(crypto.randomUUID(),e.aggregateId,{status:e.payload.status,previousStatus:e.payload.previousStatus,updatedAt:new Date().toISOString()},new Date().toISOString(),e.version+1);await this.eventStore.append(e.aggregateId,[t])}}};var wr="1.0.0";exports.AccountService = T; exports.AuthService = R; exports.AxiosHttpClient = I; exports.CircuitBreaker = L; exports.CreateShipmentUseCase = E; exports.CreateSubAccountUseCase = O; exports.ExecuteRechargeUseCase = M; exports.ExpressRateLimiter = N; exports.GetRatesUseCase = C; exports.HandleWebhookUseCase = H; exports.HelmetCspMiddleware = Q; exports.InMemoryTokenManager = x; exports.MetricsCollector = U; exports.MobileRechargeService = P; exports.OpossumCircuitBreaker = K; exports.OtelTracer = D; exports.PollingService = A; exports.SDK_VERSION = wr; exports.ShipmentCreatedEvent = _chunkRM3JFTCXcjs.d; exports.ShipmentReconciliationSaga = Z; exports.ShipmentService = _chunkRM3JFTCXcjs.a; exports.ShipmentStatusChangedEvent = _chunkRM3JFTCXcjs.e; exports.ShipmentUpdatedEvent = _chunkRM3JFTCXcjs.f; exports.SqliteEventStore = _chunkRM3JFTCXcjs.c; exports.TrackShipmentUseCase = q; exports.UpdateShipmentUseCase = _chunkRM3JFTCXcjs.b; exports.WebhookProcessedEvent = Y; exports.WebhookReceivedEvent = X; exports.bootstrap = Me; exports.gracefulShutdown = ve; exports.initOpenTelemetry = V; exports.shutdownOpenTelemetry = J;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/osamud/Escritorio/WeShipYou/dist/index.cjs","../src/infrastructure/web/server.ts","../src/infrastructure/logging/token-manager.impl.ts","../src/infrastructure/http/axios-http-client.ts","../src/domain/errors/api.error.ts"],"names":["gracefulShutdown","server","deps","timeout","timer","d","InMemoryTokenManager","token","exp","ApiError","statusCode","message","details","AuthError","RateLimitError","logger","pino","AxiosHttpClient","baseURL","tokenManager","refreshTokenFn","retries","baseDelayMs","idempotencyPaths","onRateLimit","axios","config"],"mappings":"AAAA,quBAA+E,SCM/DA,EAAAA,CAAiBC,CAAAA,CAAgBC,CAAAA,CAAsBC,CAAAA,CAAU,GAAA,CAA4B,CAC3G,OAAO,KAAA,CAAA,CAAA,EAAY,CACjB,IAAMC,CAAAA,CAAQ,UAAA,CAAW,CAAA,CAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,CAAGD,CAAO,CAAA,CACvDF,CAAAA,CAAO,KAAA,CAAM,CAAA,CACb,MAAM,OAAA,CAAQ,GAAA,CAAIC,CAAAA,CAAK,GAAA,CAAIG,CAAAA,EAAKA,CAAAA,CAAE,KAAA,CAAM,CAAA,CAAE,KAAA,CAAM,CAAA,CAAA,EAAM,CAAC,CAAC,CAAC,CAAC,CAAA,CAC1D,YAAA,CAAaD,CAAK,CAAA,CAClB,OAAA,CAAQ,IAAA,CAAK,CAAC,CAChB,CACF,CCZO,IAAME,CAAAA,CAAN,KAAoD,CAApD,WAAA,CAAA,CAAA,CACL,IAAA,CAAQ,KAAA,CAAuB,IAAA,CAC/B,IAAA,CAAQ,GAAA,CAAc,CAAA,CAEtB,QAAA,CAASC,CAAAA,CAAeC,CAAAA,CAAmB,CACzC,IAAA,CAAK,KAAA,CAAQD,CAAAA,CACb,IAAA,CAAK,GAAA,CAAMC,CACb,CAEA,QAAA,CAAA,CAA0B,CACxB,OAAO,IAAA,CAAK,KACd,CAEA,SAAA,CAAA,CAAqB,CACnB,OAAK,IAAA,CAAK,GAAA,CACF,IAAA,CAAK,GAAA,CAAM,GAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,CADd,CAAA,CAExB,CAEA,KAAA,CAAA,CAAc,CACZ,IAAA,CAAK,KAAA,CAAQ,IAAA,CACb,IAAA,CAAK,GAAA,CAAM,CACb,CACF,CAAA,CCxBA,4EAAyD,ICA5CC,CAAAA,CAAN,MAAA,QAAuB,KAAM,CAClC,WAAA,CACkBC,CAAAA,CAChBC,CAAAA,CACgBC,CAAAA,CAChB,CACA,KAAA,CAAMD,CAAO,CAAA,CAJG,IAAA,CAAA,UAAA,CAAAD,CAAAA,CAEA,IAAA,CAAA,OAAA,CAAAE,CAAAA,CAGhB,IAAA,CAAK,IAAA,CAAO,UACd,CACF,CAAA,CAEaC,CAAAA,CAAN,MAAA,QAAwBJ,CAAS,CACtC,WAAA,CAAYE,CAAAA,CAAU,uBAAA,CAAyB,CAC7C,KAAA,CAAM,GAAA,CAAKA,CAAO,CACpB,CACF,CAAA,CAEaG,CAAAA,CAAN,MAAA,QAA6BL,CAAS,CAC3C,WAAA,CAAYE,CAAAA,CAAU,qBAAA,CAAuB,CAC3C,KAAA,CAAM,GAAA,CAAKA,CAAO,CACpB,CACF,CAAA,CDjBA,gCAA2B,wEACV,IAEXI,EAAAA,CAASC,4BAAAA,CAAO,KAAA,CAAO,MAAO,CAAC,CAAA,CAExBC,CAAAA,2BAAN,KAA6C,CAYlD,WAAA,CACEC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CAAU,CAAA,CACVC,CAAAA,CAAc,GAAA,CACdC,CAAAA,CAA6B,CAAC,YAAA,CAAc,mBAAA,CAAqB,YAAY,CAAA,CAC7EC,CAAAA,CACA,CAdF,IAAA,CAAQ,cAAA,CAAuE,IAAA,CAI/E,IAAA,CAAA,aAAA,CAA4E,IAAA,CAW1E,IAAA,CAAK,MAAA,CAASC,eAAAA,CAAM,MAAA,CAAO,CAAE,OAAA,CAAAP,CAAAA,CAAS,OAAA,CAAS,GAAM,CAAC,CAAA,CACtD,IAAA,CAAK,YAAA,CAAeC,CAAAA,CACpB,IAAA,CAAK,cAAA,CAAiBC,CAAAA,CACtB,IAAA,CAAK,OAAA,CAAUC,CAAAA,CACf,IAAA,CAAK,WAAA,CAAcC,CAAAA,CACnB,IAAA,CAAK,gBAAA,CAAmBC,CAAAA,CACxB,IAAA,CAAK,WAAA,CAAcC,CAAAA,CAEnB,IAAA,CAAK,MAAA,CAAO,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAIE,CAAAA,EAAU,CAC7C,IAAMnB,CAAAA,CAAQ,IAAA,CAAK,YAAA,CAAa,QAAA,CAAS,CAAA,CACzC,OAAIA,CAAAA,EAAS,CAACmB,CAAAA,CAAO,OAAA,CAAQ,aAAA,EAAA,CAC3BA,CAAAA,CAAO,OAAA,CAAQ,aAAA,CAAmB,CAAA,OAAA,EAAUnB,CAAK,CAAA,CAAA","file":"/home/osamud/Escritorio/WeShipYou/dist/index.cjs","sourcesContent":[null,"import { Server } from 'http';\n\nexport interface Shutdownable {\n close(): Promise<void>;\n}\n\nexport function gracefulShutdown(server: Server, deps: Shutdownable[], timeout = 30000): () => Promise<void> {\n return async () => {\n const timer = setTimeout(() => process.exit(1), timeout);\n server.close();\n await Promise.all(deps.map(d => d.close().catch(() => {})));\n clearTimeout(timer);\n process.exit(0);\n };\n}\n","import { ITokenManager } from '../../domain/interfaces/token-manager.interface';\n\nexport class InMemoryTokenManager implements ITokenManager {\n private token: string | null = null;\n private exp: number = 0;\n\n setToken(token: string, exp: number): void {\n this.token = token;\n this.exp = exp;\n }\n\n getToken(): string | null {\n return this.token;\n }\n\n isExpired(): boolean {\n if (!this.exp) return true;\n return (this.exp * 1000) < Date.now();\n }\n\n clear(): void {\n this.token = null;\n this.exp = 0;\n }\n}\n","import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';\nimport { IHttpClient, HttpRequest, HttpResponse } from '../../domain/interfaces/http-client.interface';\nimport { ITokenManager } from '../../domain/interfaces/token-manager.interface';\nimport { ApiError, RateLimitError, AuthError } from '../../domain/errors/api.error';\nimport { randomUUID } from 'crypto';\nimport pino from 'pino';\n\nconst logger = pino({ level: 'info' });\n\nexport class AxiosHttpClient implements IHttpClient {\n private client: AxiosInstance;\n private tokenManager: ITokenManager;\n private retries: number;\n private baseDelayMs: number;\n private refreshTokenFn?: () => Promise<{ accessToken: string; exp: number }>;\n private refreshPromise: Promise<{ accessToken: string; exp: number }> | null = null;\n private idempotencyPaths: RegExp[];\n private onRateLimit?: (info: { remaining: number; limit: number; reset: number }) => void;\n\n lastRateLimit: { remaining: number; limit: number; reset: number } | null = null;\n\n constructor(\n baseURL: string,\n tokenManager: ITokenManager,\n refreshTokenFn?: () => Promise<{ accessToken: string; exp: number }>,\n retries = 3,\n baseDelayMs = 1000,\n idempotencyPaths: RegExp[] = [/add-funds/i, /mobile-recharges/i, /recharges/i],\n onRateLimit?: (info: { remaining: number; limit: number; reset: number }) => void\n ) {\n this.client = axios.create({ baseURL, timeout: 10000 });\n this.tokenManager = tokenManager;\n this.refreshTokenFn = refreshTokenFn;\n this.retries = retries;\n this.baseDelayMs = baseDelayMs;\n this.idempotencyPaths = idempotencyPaths;\n this.onRateLimit = onRateLimit;\n\n this.client.interceptors.request.use(config => {\n const token = this.tokenManager.getToken();\n if (token && !config.headers['Authorization']) {\n config.headers['Authorization'] = `Bearer ${token}`;\n }\n return config;\n });\n\n this.client.interceptors.response.use(\n res => res,\n async err => {\n const originalRequest = err.config;\n if (err.response?.status === 401 && !originalRequest._retry) {\n originalRequest._retry = true;\n if (this.refreshTokenFn) {\n try {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refreshTokenFn();\n }\n const { accessToken, exp } = await this.refreshPromise;\n this.refreshPromise = null;\n this.tokenManager.setToken(accessToken, exp);\n originalRequest.headers['Authorization'] = `Bearer ${accessToken}`;\n return this.client(originalRequest);\n } catch (refreshErr) {\n this.refreshPromise = null;\n logger.warn('Token refresh failed');\n this.tokenManager.clear();\n throw new AuthError('Session expired');\n }\n }\n }\n\n if (err.response) {\n const { status, data } = err.response;\n if (status === 429) throw new RateLimitError(data?.message);\n if (status >= 400) throw new ApiError(status, data?.message || 'API request failed', data);\n }\n throw err;\n }\n );\n }\n\n getRateLimitInfo(): { remaining: number; limit: number; reset: number } | null {\n return this.lastRateLimit;\n }\n\n async request<T>(config: HttpRequest): Promise<HttpResponse<T>> {\n let attempt = 0;\n while (attempt < this.retries) {\n try {\n const axiosConfig: AxiosRequestConfig = {\n method: config.method.toLowerCase(),\n url: config.url,\n data: config.body,\n headers: { ...config.headers }\n };\n\n if (config.method === 'POST') {\n const matchesIdempotency = this.idempotencyPaths.some(pattern => pattern.test(config.url));\n if (matchesIdempotency) {\n axiosConfig.headers = { ...(axiosConfig.headers || {}), 'Idempotency-Key': randomUUID() };\n }\n }\n\n const response = await this.client.request(axiosConfig);\n\n const remaining = response.headers['x-ratelimit-remaining'];\n const limit = response.headers['x-ratelimit-limit'];\n const reset = response.headers['x-ratelimit-reset'];\n if (remaining !== undefined && limit !== undefined && reset !== undefined) {\n this.lastRateLimit = {\n remaining: Number(remaining),\n limit: Number(limit),\n reset: Number(reset),\n };\n this.onRateLimit?.(this.lastRateLimit);\n }\n\n return {\n status: response.status,\n data: response.data as T,\n headers: response.headers as Record<string, string>\n };\n } catch (error: any) {\n attempt++;\n const isRetryable = error instanceof RateLimitError\n || error?.response?.status >= 500\n || error?.statusCode >= 500;\n if (attempt >= this.retries || !isRetryable) {\n throw error;\n }\n await new Promise(res => setTimeout(res, this.baseDelayMs * Math.pow(2, attempt - 1)));\n }\n }\n throw new Error('Max retries exceeded');\n }\n}\n","export class ApiError extends Error {\n constructor(\n public readonly statusCode: number,\n message: string,\n public readonly details?: unknown\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport class AuthError extends ApiError {\n constructor(message = 'Authentication failed') {\n super(401, message);\n }\n}\n\nexport class RateLimitError extends ApiError {\n constructor(message = 'Rate limit exceeded') {\n super(429, message);\n }\n}\n"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { I as IHttpClient, d as ITokenManager, H as HttpRequest, e as HttpResponse, f as ICircuitBreaker, g as CircuitState, h as IRateLimiter, i as ITracer, j as ISpan, D as DomainEvent, b as IEventStore } from './update-shipment.use-case-BsHiVqQV.mjs';
|
|
2
|
+
export { k as AccountService, l as AuthService, C as CreateShipmentUseCase, m as CreateSubAccountUseCase, n as DimUnit, E as ExecuteRechargeUseCase, G as GetRatesUseCase, o as HandleWebhookUseCase, p as IPollingService, q as IRateRequest, a as IShipmentRequest, r as IWebhookEvent, s as IWebhookHandler, t as Incoterms, M as MetricsCollector, u as MobileRechargeService, P as Parcel, v as ParcelItem, w as PollingOptions, x as PollingService, R as RatesAddress, y as SenderRecipientBase, c as Services, z as ShipmentRecipient, B as ShipmentSender, F as ShipmentService, J as ShipmentStatus, S as SqliteEventStore, T as TrackShipmentUseCase, U as UpdateShipmentUseCase, W as WebhookConfig, K as WeightUnit, L as bootstrap } from './update-shipment.use-case-BsHiVqQV.mjs';
|
|
3
|
+
import { RequestHandler, Request, Response, NextFunction } from 'express';
|
|
4
|
+
import { Server } from 'http';
|
|
5
|
+
import { Options } from 'express-rate-limit';
|
|
6
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
7
|
+
import 'zod';
|
|
8
|
+
|
|
9
|
+
interface ISecurityMiddleware {
|
|
10
|
+
getHandler(): RequestHandler;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface IPaginatedResponse<T> {
|
|
14
|
+
data: T[];
|
|
15
|
+
total: number;
|
|
16
|
+
limit: number;
|
|
17
|
+
skip: number;
|
|
18
|
+
}
|
|
19
|
+
interface IQueryPagination {
|
|
20
|
+
limit?: number;
|
|
21
|
+
skip?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type SdkEnvironment = 'test' | 'production';
|
|
25
|
+
|
|
26
|
+
interface Shutdownable {
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
declare function gracefulShutdown(server: Server, deps: Shutdownable[], timeout?: number): () => Promise<void>;
|
|
30
|
+
|
|
31
|
+
declare class AxiosHttpClient implements IHttpClient {
|
|
32
|
+
private client;
|
|
33
|
+
private tokenManager;
|
|
34
|
+
private retries;
|
|
35
|
+
private baseDelayMs;
|
|
36
|
+
private refreshTokenFn?;
|
|
37
|
+
private refreshPromise;
|
|
38
|
+
private idempotencyPaths;
|
|
39
|
+
private onRateLimit?;
|
|
40
|
+
lastRateLimit: {
|
|
41
|
+
remaining: number;
|
|
42
|
+
limit: number;
|
|
43
|
+
reset: number;
|
|
44
|
+
} | null;
|
|
45
|
+
constructor(baseURL: string, tokenManager: ITokenManager, refreshTokenFn?: () => Promise<{
|
|
46
|
+
accessToken: string;
|
|
47
|
+
exp: number;
|
|
48
|
+
}>, retries?: number, baseDelayMs?: number, idempotencyPaths?: RegExp[], onRateLimit?: (info: {
|
|
49
|
+
remaining: number;
|
|
50
|
+
limit: number;
|
|
51
|
+
reset: number;
|
|
52
|
+
}) => void);
|
|
53
|
+
getRateLimitInfo(): {
|
|
54
|
+
remaining: number;
|
|
55
|
+
limit: number;
|
|
56
|
+
reset: number;
|
|
57
|
+
} | null;
|
|
58
|
+
request<T>(config: HttpRequest): Promise<HttpResponse<T>>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare class InMemoryTokenManager implements ITokenManager {
|
|
62
|
+
private token;
|
|
63
|
+
private exp;
|
|
64
|
+
setToken(token: string, exp: number): void;
|
|
65
|
+
getToken(): string | null;
|
|
66
|
+
isExpired(): boolean;
|
|
67
|
+
clear(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare class CircuitBreaker implements ICircuitBreaker {
|
|
71
|
+
private state;
|
|
72
|
+
private failures;
|
|
73
|
+
private failureThreshold;
|
|
74
|
+
private resetTimeoutMs;
|
|
75
|
+
private lastFailureTime;
|
|
76
|
+
constructor(failureThreshold?: number, resetTimeoutMs?: number);
|
|
77
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
78
|
+
getState(): CircuitState;
|
|
79
|
+
reset(): void;
|
|
80
|
+
private onSuccess;
|
|
81
|
+
private onFailure;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
declare class OpossumCircuitBreaker implements ICircuitBreaker {
|
|
85
|
+
private breaker;
|
|
86
|
+
constructor(options?: {
|
|
87
|
+
timeout?: number;
|
|
88
|
+
errorThresholdPercentage?: number;
|
|
89
|
+
resetTimeout?: number;
|
|
90
|
+
});
|
|
91
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
92
|
+
getState(): CircuitState;
|
|
93
|
+
reset(): void;
|
|
94
|
+
getMetrics(): {
|
|
95
|
+
success: number;
|
|
96
|
+
failure: number;
|
|
97
|
+
timeout: number;
|
|
98
|
+
rejection: number;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
declare class ExpressRateLimiter implements IRateLimiter {
|
|
103
|
+
private limiter;
|
|
104
|
+
constructor(opts?: Partial<Options>);
|
|
105
|
+
consume(_key: string, _tokens?: number): Promise<{
|
|
106
|
+
remaining: number;
|
|
107
|
+
limit: number;
|
|
108
|
+
retryAfter?: number;
|
|
109
|
+
}>;
|
|
110
|
+
getMiddleware(): (req: Request, res: Response, next: NextFunction) => unknown;
|
|
111
|
+
close(): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
declare class OtelTracer implements ITracer {
|
|
115
|
+
private tracer;
|
|
116
|
+
startSpan(name: string, attributes?: Record<string, string | number | boolean>): ISpan;
|
|
117
|
+
close(): Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
declare function initOpenTelemetry(serviceName?: string): NodeSDK;
|
|
121
|
+
declare function shutdownOpenTelemetry(): Promise<void>;
|
|
122
|
+
|
|
123
|
+
declare class HelmetCspMiddleware implements ISecurityMiddleware {
|
|
124
|
+
getHandler(): RequestHandler;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
declare class ShipmentCreatedEvent implements DomainEvent {
|
|
128
|
+
readonly eventId: string;
|
|
129
|
+
readonly aggregateId: string;
|
|
130
|
+
readonly payload: {
|
|
131
|
+
accountId: string;
|
|
132
|
+
trackingNumber?: string;
|
|
133
|
+
};
|
|
134
|
+
readonly timestamp: string;
|
|
135
|
+
readonly version: number;
|
|
136
|
+
readonly type = "shipment.created";
|
|
137
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
138
|
+
accountId: string;
|
|
139
|
+
trackingNumber?: string;
|
|
140
|
+
}, timestamp?: string, version?: number);
|
|
141
|
+
}
|
|
142
|
+
declare class ShipmentStatusChangedEvent implements DomainEvent {
|
|
143
|
+
readonly eventId: string;
|
|
144
|
+
readonly aggregateId: string;
|
|
145
|
+
readonly payload: {
|
|
146
|
+
status: string;
|
|
147
|
+
previousStatus: string;
|
|
148
|
+
updatedAt: string;
|
|
149
|
+
};
|
|
150
|
+
readonly timestamp: string;
|
|
151
|
+
readonly version: number;
|
|
152
|
+
readonly type = "shipment.status_changed";
|
|
153
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
154
|
+
status: string;
|
|
155
|
+
previousStatus: string;
|
|
156
|
+
updatedAt: string;
|
|
157
|
+
}, timestamp?: string, version?: number);
|
|
158
|
+
}
|
|
159
|
+
declare class ShipmentUpdatedEvent implements DomainEvent {
|
|
160
|
+
readonly eventId: string;
|
|
161
|
+
readonly aggregateId: string;
|
|
162
|
+
readonly payload: {
|
|
163
|
+
accountId: string;
|
|
164
|
+
fields: string[];
|
|
165
|
+
};
|
|
166
|
+
readonly timestamp: string;
|
|
167
|
+
readonly version: number;
|
|
168
|
+
readonly type = "shipment.updated";
|
|
169
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
170
|
+
accountId: string;
|
|
171
|
+
fields: string[];
|
|
172
|
+
}, timestamp?: string, version?: number);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
declare class WebhookReceivedEvent implements DomainEvent {
|
|
176
|
+
readonly eventId: string;
|
|
177
|
+
readonly aggregateId: string;
|
|
178
|
+
readonly payload: {
|
|
179
|
+
eventType: string;
|
|
180
|
+
signature: string;
|
|
181
|
+
body: string;
|
|
182
|
+
};
|
|
183
|
+
readonly timestamp: string;
|
|
184
|
+
readonly version: number;
|
|
185
|
+
readonly type = "webhook.received";
|
|
186
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
187
|
+
eventType: string;
|
|
188
|
+
signature: string;
|
|
189
|
+
body: string;
|
|
190
|
+
}, timestamp?: string, version?: number);
|
|
191
|
+
}
|
|
192
|
+
declare class WebhookProcessedEvent implements DomainEvent {
|
|
193
|
+
readonly eventId: string;
|
|
194
|
+
readonly aggregateId: string;
|
|
195
|
+
readonly payload: {
|
|
196
|
+
eventType: string;
|
|
197
|
+
success: boolean;
|
|
198
|
+
error?: string;
|
|
199
|
+
};
|
|
200
|
+
readonly timestamp: string;
|
|
201
|
+
readonly version: number;
|
|
202
|
+
readonly type = "webhook.processed";
|
|
203
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
204
|
+
eventType: string;
|
|
205
|
+
success: boolean;
|
|
206
|
+
error?: string;
|
|
207
|
+
}, timestamp?: string, version?: number);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
declare class ShipmentReconciliationSaga {
|
|
211
|
+
private readonly eventStore;
|
|
212
|
+
private readonly httpClient;
|
|
213
|
+
constructor(eventStore: IEventStore, httpClient: IHttpClient);
|
|
214
|
+
onShipmentCreated(event: ShipmentCreatedEvent): Promise<void>;
|
|
215
|
+
onShipmentStatusChanged(event: ShipmentStatusChangedEvent): Promise<void>;
|
|
216
|
+
onWebhookReceived(event: DomainEvent): Promise<void>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare const SDK_VERSION = "1.0.0";
|
|
220
|
+
|
|
221
|
+
export { AxiosHttpClient, CircuitBreaker, CircuitState, DomainEvent, ExpressRateLimiter, HelmetCspMiddleware, HttpRequest, HttpResponse, ICircuitBreaker, IEventStore, IHttpClient, type IPaginatedResponse, type IQueryPagination, IRateLimiter, type ISecurityMiddleware, ISpan, ITokenManager, ITracer, InMemoryTokenManager, OpossumCircuitBreaker, OtelTracer, SDK_VERSION, type SdkEnvironment, ShipmentCreatedEvent, ShipmentReconciliationSaga, ShipmentStatusChangedEvent, ShipmentUpdatedEvent, type Shutdownable, WebhookProcessedEvent, WebhookReceivedEvent, gracefulShutdown, initOpenTelemetry, shutdownOpenTelemetry };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { I as IHttpClient, d as ITokenManager, H as HttpRequest, e as HttpResponse, f as ICircuitBreaker, g as CircuitState, h as IRateLimiter, i as ITracer, j as ISpan, D as DomainEvent, b as IEventStore } from './update-shipment.use-case-BsHiVqQV.js';
|
|
2
|
+
export { k as AccountService, l as AuthService, C as CreateShipmentUseCase, m as CreateSubAccountUseCase, n as DimUnit, E as ExecuteRechargeUseCase, G as GetRatesUseCase, o as HandleWebhookUseCase, p as IPollingService, q as IRateRequest, a as IShipmentRequest, r as IWebhookEvent, s as IWebhookHandler, t as Incoterms, M as MetricsCollector, u as MobileRechargeService, P as Parcel, v as ParcelItem, w as PollingOptions, x as PollingService, R as RatesAddress, y as SenderRecipientBase, c as Services, z as ShipmentRecipient, B as ShipmentSender, F as ShipmentService, J as ShipmentStatus, S as SqliteEventStore, T as TrackShipmentUseCase, U as UpdateShipmentUseCase, W as WebhookConfig, K as WeightUnit, L as bootstrap } from './update-shipment.use-case-BsHiVqQV.js';
|
|
3
|
+
import { RequestHandler, Request, Response, NextFunction } from 'express';
|
|
4
|
+
import { Server } from 'http';
|
|
5
|
+
import { Options } from 'express-rate-limit';
|
|
6
|
+
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
7
|
+
import 'zod';
|
|
8
|
+
|
|
9
|
+
interface ISecurityMiddleware {
|
|
10
|
+
getHandler(): RequestHandler;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface IPaginatedResponse<T> {
|
|
14
|
+
data: T[];
|
|
15
|
+
total: number;
|
|
16
|
+
limit: number;
|
|
17
|
+
skip: number;
|
|
18
|
+
}
|
|
19
|
+
interface IQueryPagination {
|
|
20
|
+
limit?: number;
|
|
21
|
+
skip?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type SdkEnvironment = 'test' | 'production';
|
|
25
|
+
|
|
26
|
+
interface Shutdownable {
|
|
27
|
+
close(): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
declare function gracefulShutdown(server: Server, deps: Shutdownable[], timeout?: number): () => Promise<void>;
|
|
30
|
+
|
|
31
|
+
declare class AxiosHttpClient implements IHttpClient {
|
|
32
|
+
private client;
|
|
33
|
+
private tokenManager;
|
|
34
|
+
private retries;
|
|
35
|
+
private baseDelayMs;
|
|
36
|
+
private refreshTokenFn?;
|
|
37
|
+
private refreshPromise;
|
|
38
|
+
private idempotencyPaths;
|
|
39
|
+
private onRateLimit?;
|
|
40
|
+
lastRateLimit: {
|
|
41
|
+
remaining: number;
|
|
42
|
+
limit: number;
|
|
43
|
+
reset: number;
|
|
44
|
+
} | null;
|
|
45
|
+
constructor(baseURL: string, tokenManager: ITokenManager, refreshTokenFn?: () => Promise<{
|
|
46
|
+
accessToken: string;
|
|
47
|
+
exp: number;
|
|
48
|
+
}>, retries?: number, baseDelayMs?: number, idempotencyPaths?: RegExp[], onRateLimit?: (info: {
|
|
49
|
+
remaining: number;
|
|
50
|
+
limit: number;
|
|
51
|
+
reset: number;
|
|
52
|
+
}) => void);
|
|
53
|
+
getRateLimitInfo(): {
|
|
54
|
+
remaining: number;
|
|
55
|
+
limit: number;
|
|
56
|
+
reset: number;
|
|
57
|
+
} | null;
|
|
58
|
+
request<T>(config: HttpRequest): Promise<HttpResponse<T>>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
declare class InMemoryTokenManager implements ITokenManager {
|
|
62
|
+
private token;
|
|
63
|
+
private exp;
|
|
64
|
+
setToken(token: string, exp: number): void;
|
|
65
|
+
getToken(): string | null;
|
|
66
|
+
isExpired(): boolean;
|
|
67
|
+
clear(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare class CircuitBreaker implements ICircuitBreaker {
|
|
71
|
+
private state;
|
|
72
|
+
private failures;
|
|
73
|
+
private failureThreshold;
|
|
74
|
+
private resetTimeoutMs;
|
|
75
|
+
private lastFailureTime;
|
|
76
|
+
constructor(failureThreshold?: number, resetTimeoutMs?: number);
|
|
77
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
78
|
+
getState(): CircuitState;
|
|
79
|
+
reset(): void;
|
|
80
|
+
private onSuccess;
|
|
81
|
+
private onFailure;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
declare class OpossumCircuitBreaker implements ICircuitBreaker {
|
|
85
|
+
private breaker;
|
|
86
|
+
constructor(options?: {
|
|
87
|
+
timeout?: number;
|
|
88
|
+
errorThresholdPercentage?: number;
|
|
89
|
+
resetTimeout?: number;
|
|
90
|
+
});
|
|
91
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
92
|
+
getState(): CircuitState;
|
|
93
|
+
reset(): void;
|
|
94
|
+
getMetrics(): {
|
|
95
|
+
success: number;
|
|
96
|
+
failure: number;
|
|
97
|
+
timeout: number;
|
|
98
|
+
rejection: number;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
declare class ExpressRateLimiter implements IRateLimiter {
|
|
103
|
+
private limiter;
|
|
104
|
+
constructor(opts?: Partial<Options>);
|
|
105
|
+
consume(_key: string, _tokens?: number): Promise<{
|
|
106
|
+
remaining: number;
|
|
107
|
+
limit: number;
|
|
108
|
+
retryAfter?: number;
|
|
109
|
+
}>;
|
|
110
|
+
getMiddleware(): (req: Request, res: Response, next: NextFunction) => unknown;
|
|
111
|
+
close(): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
declare class OtelTracer implements ITracer {
|
|
115
|
+
private tracer;
|
|
116
|
+
startSpan(name: string, attributes?: Record<string, string | number | boolean>): ISpan;
|
|
117
|
+
close(): Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
declare function initOpenTelemetry(serviceName?: string): NodeSDK;
|
|
121
|
+
declare function shutdownOpenTelemetry(): Promise<void>;
|
|
122
|
+
|
|
123
|
+
declare class HelmetCspMiddleware implements ISecurityMiddleware {
|
|
124
|
+
getHandler(): RequestHandler;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
declare class ShipmentCreatedEvent implements DomainEvent {
|
|
128
|
+
readonly eventId: string;
|
|
129
|
+
readonly aggregateId: string;
|
|
130
|
+
readonly payload: {
|
|
131
|
+
accountId: string;
|
|
132
|
+
trackingNumber?: string;
|
|
133
|
+
};
|
|
134
|
+
readonly timestamp: string;
|
|
135
|
+
readonly version: number;
|
|
136
|
+
readonly type = "shipment.created";
|
|
137
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
138
|
+
accountId: string;
|
|
139
|
+
trackingNumber?: string;
|
|
140
|
+
}, timestamp?: string, version?: number);
|
|
141
|
+
}
|
|
142
|
+
declare class ShipmentStatusChangedEvent implements DomainEvent {
|
|
143
|
+
readonly eventId: string;
|
|
144
|
+
readonly aggregateId: string;
|
|
145
|
+
readonly payload: {
|
|
146
|
+
status: string;
|
|
147
|
+
previousStatus: string;
|
|
148
|
+
updatedAt: string;
|
|
149
|
+
};
|
|
150
|
+
readonly timestamp: string;
|
|
151
|
+
readonly version: number;
|
|
152
|
+
readonly type = "shipment.status_changed";
|
|
153
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
154
|
+
status: string;
|
|
155
|
+
previousStatus: string;
|
|
156
|
+
updatedAt: string;
|
|
157
|
+
}, timestamp?: string, version?: number);
|
|
158
|
+
}
|
|
159
|
+
declare class ShipmentUpdatedEvent implements DomainEvent {
|
|
160
|
+
readonly eventId: string;
|
|
161
|
+
readonly aggregateId: string;
|
|
162
|
+
readonly payload: {
|
|
163
|
+
accountId: string;
|
|
164
|
+
fields: string[];
|
|
165
|
+
};
|
|
166
|
+
readonly timestamp: string;
|
|
167
|
+
readonly version: number;
|
|
168
|
+
readonly type = "shipment.updated";
|
|
169
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
170
|
+
accountId: string;
|
|
171
|
+
fields: string[];
|
|
172
|
+
}, timestamp?: string, version?: number);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
declare class WebhookReceivedEvent implements DomainEvent {
|
|
176
|
+
readonly eventId: string;
|
|
177
|
+
readonly aggregateId: string;
|
|
178
|
+
readonly payload: {
|
|
179
|
+
eventType: string;
|
|
180
|
+
signature: string;
|
|
181
|
+
body: string;
|
|
182
|
+
};
|
|
183
|
+
readonly timestamp: string;
|
|
184
|
+
readonly version: number;
|
|
185
|
+
readonly type = "webhook.received";
|
|
186
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
187
|
+
eventType: string;
|
|
188
|
+
signature: string;
|
|
189
|
+
body: string;
|
|
190
|
+
}, timestamp?: string, version?: number);
|
|
191
|
+
}
|
|
192
|
+
declare class WebhookProcessedEvent implements DomainEvent {
|
|
193
|
+
readonly eventId: string;
|
|
194
|
+
readonly aggregateId: string;
|
|
195
|
+
readonly payload: {
|
|
196
|
+
eventType: string;
|
|
197
|
+
success: boolean;
|
|
198
|
+
error?: string;
|
|
199
|
+
};
|
|
200
|
+
readonly timestamp: string;
|
|
201
|
+
readonly version: number;
|
|
202
|
+
readonly type = "webhook.processed";
|
|
203
|
+
constructor(eventId: string, aggregateId: string, payload: {
|
|
204
|
+
eventType: string;
|
|
205
|
+
success: boolean;
|
|
206
|
+
error?: string;
|
|
207
|
+
}, timestamp?: string, version?: number);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
declare class ShipmentReconciliationSaga {
|
|
211
|
+
private readonly eventStore;
|
|
212
|
+
private readonly httpClient;
|
|
213
|
+
constructor(eventStore: IEventStore, httpClient: IHttpClient);
|
|
214
|
+
onShipmentCreated(event: ShipmentCreatedEvent): Promise<void>;
|
|
215
|
+
onShipmentStatusChanged(event: ShipmentStatusChangedEvent): Promise<void>;
|
|
216
|
+
onWebhookReceived(event: DomainEvent): Promise<void>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare const SDK_VERSION = "1.0.0";
|
|
220
|
+
|
|
221
|
+
export { AxiosHttpClient, CircuitBreaker, CircuitState, DomainEvent, ExpressRateLimiter, HelmetCspMiddleware, HttpRequest, HttpResponse, ICircuitBreaker, IEventStore, IHttpClient, type IPaginatedResponse, type IQueryPagination, IRateLimiter, type ISecurityMiddleware, ISpan, ITokenManager, ITracer, InMemoryTokenManager, OpossumCircuitBreaker, OtelTracer, SDK_VERSION, type SdkEnvironment, ShipmentCreatedEvent, ShipmentReconciliationSaga, ShipmentStatusChangedEvent, ShipmentUpdatedEvent, type Shutdownable, WebhookProcessedEvent, WebhookReceivedEvent, gracefulShutdown, initOpenTelemetry, shutdownOpenTelemetry };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as $,b as ge,c as fe,d as Se,e as _,f as ye}from"./chunk-KT3VLIZI.mjs";function ve(o,e,t=3e4){return async()=>{let r=setTimeout(()=>process.exit(1),t);o.close(),await Promise.all(e.map(n=>n.close().catch(()=>{}))),clearTimeout(r),process.exit(0)}}var x=class{constructor(){this.token=null;this.exp=0}setToken(e,t){this.token=e,this.exp=t}getToken(){return this.token}isExpired(){return this.exp?this.exp*1e3<Date.now():!0}clear(){this.token=null,this.exp=0}};import be from"axios";var y=class extends Error{constructor(t,r,n){super(r);this.statusCode=t;this.details=n;this.name="ApiError"}},F=class extends y{constructor(e="Authentication failed"){super(401,e)}},k=class extends y{constructor(e="Rate limit exceeded"){super(429,e)}};import{randomUUID as we}from"crypto";import xe from"pino";var ke=xe({level:"info"}),I=class{constructor(e,t,r,n=3,i=1e3,m=[/add-funds/i,/mobile-recharges/i,/recharges/i],p){this.refreshPromise=null;this.lastRateLimit=null;this.client=be.create({baseURL:e,timeout:1e4}),this.tokenManager=t,this.refreshTokenFn=r,this.retries=n,this.baseDelayMs=i,this.idempotencyPaths=m,this.onRateLimit=p,this.client.interceptors.request.use(s=>{let a=this.tokenManager.getToken();return a&&!s.headers.Authorization&&(s.headers.Authorization=`Bearer ${a}`),s}),this.client.interceptors.response.use(s=>s,async s=>{let a=s.config;if(s.response?.status===401&&!a._retry&&(a._retry=!0,this.refreshTokenFn))try{this.refreshPromise||(this.refreshPromise=this.refreshTokenFn());let{accessToken:c,exp:l}=await this.refreshPromise;return this.refreshPromise=null,this.tokenManager.setToken(c,l),a.headers.Authorization=`Bearer ${c}`,this.client(a)}catch{throw this.refreshPromise=null,ke.warn("Token refresh failed"),this.tokenManager.clear(),new F("Session expired")}if(s.response){let{status:c,data:l}=s.response;if(c===429)throw new k(l?.message);if(c>=400)throw new y(c,l?.message||"API request failed",l)}throw s})}getRateLimitInfo(){return this.lastRateLimit}async request(e){let t=0;for(;t<this.retries;)try{let r={method:e.method.toLowerCase(),url:e.url,data:e.body,headers:{...e.headers}};e.method==="POST"&&this.idempotencyPaths.some(a=>a.test(e.url))&&(r.headers={...r.headers||{},"Idempotency-Key":we()});let n=await this.client.request(r),i=n.headers["x-ratelimit-remaining"],m=n.headers["x-ratelimit-limit"],p=n.headers["x-ratelimit-reset"];return i!==void 0&&m!==void 0&&p!==void 0&&(this.lastRateLimit={remaining:Number(i),limit:Number(m),reset:Number(p)},this.onRateLimit?.(this.lastRateLimit)),{status:n.status,data:n.data,headers:n.headers}}catch(r){t++;let n=r instanceof k||r?.response?.status>=500||r?.statusCode>=500;if(t>=this.retries||!n)throw r;await new Promise(i=>setTimeout(i,this.baseDelayMs*Math.pow(2,t-1)))}throw new Error("Max retries exceeded")}};var R=class{constructor(e,t){this.httpClient=e;this.tokenManager=t}async authenticate(e,t,r="en-US"){let i=(await this.httpClient.request({method:"POST",url:"/api/v1/authentication",body:{username:e,password:t},headers:{"Accept-Language":r}})).data;return this.tokenManager&&i.accessToken&&this.tokenManager.setToken(i.accessToken,i.exp||0),i}};var T=class{constructor(e){this.httpClient=e}async addSubAccount(e,t="en-US"){return(await this.httpClient.request({method:"POST",url:"/api/v1/add-sub-account",body:e,headers:{"Accept-Language":t}})).data}async addFunds(e,t="en-US"){return(await this.httpClient.request({method:"POST",url:"/api/v1/add-funds",body:{paymentMethod:"zelle",...e},headers:{"Accept-Language":t}})).data}};var P=class{constructor(e){this.httpClient=e}async createRecharge(e,t="en-US"){return(await this.httpClient.request({method:"POST",url:"/api/v1/mobile-recharges/recharges",body:e,headers:{"Accept-Language":t}})).data}async getRecharges(e=20,t="en-US"){return(await this.httpClient.request({method:"GET",url:`/api/v1/mobile-recharges/recharges?limit=${encodeURIComponent(String(e))}`,headers:{"Accept-Language":t}})).data}};var A=class{async pollUntil(e,t,r={}){let{maxAttempts:n=5,baseDelayMs:i=1e3,abortSignal:m}=r,p=0;for(;p<n;){if(m?.aborted)throw new DOMException("Polling aborted","AbortError");let s=await e();if(t(s))return s;if(p++,p>=n)throw new Error(`Polling limit reached (${n})`);let a=i*Math.pow(2,p-1);await new Promise(c=>setTimeout(c,a))}throw new Error("Unreachable polling path")}};var C=class{constructor(e,t){this.authService=e;this.shipmentService=t}async execute(e,t,r,n="en-US"){return await this.authService.authenticate(e,t,n),this.shipmentService.getRates(r,n)}};var E=class{constructor(e,t){this.authService=e;this.shipmentService=t}async execute(e,t,r,n="en-US"){return await this.authService.authenticate(e,t,n),this.shipmentService.createShipment(r,n)}};import{z as u}from"zod";var ie=u.object({accountUid:u.string().optional(),externalAccountNumber:u.string().optional(),name:u.string().min(1),displayName:u.string().min(1),systemUnits:u.enum(["imperial","metric"]),email:u.string().email(),notificationEmail:u.string().email(),website:u.string().url().optional().or(u.literal("")),phone:u.string().min(6),emergencyPhone:u.string().min(6),taxId:u.string().optional(),taxIdCountry:u.object({isoCode:u.string().length(2)}).optional(),isBusiness:u.boolean().default(!1),allowBankAccountPayments:u.boolean().default(!1),address:u.object({street:u.string(),city:u.string(),state:u.string(),postalCode:u.string(),countryIsoCode:u.string().length(2)}).optional()});var v=class extends Error{constructor(t){super("Validation failed");this.details=t;this.name="ValidationError"}};var O=class{constructor(e,t){this.authService=e;this.accountService=t}async execute(e,t,r,n="en-US"){let i=ie.safeParse(r);if(!i.success)throw new v(i.error.flatten().fieldErrors);return await this.authService.authenticate(e,t,n),this.accountService.addSubAccount(i.data,n)}};import{z as h}from"zod";var oe=h.object({paymentMethod:h.enum(["zelle","credit_card","balance"]).default("zelle"),accountUid:h.string().optional(),rechargeable_product:h.object({id:h.number().int().positive()}),scheduleDate:h.string().datetime().optional().nullable(),amount:h.number().positive(),account_rechargeable_contact:h.object({name:h.string().min(1),accountNumber:h.string().min(1)})});var M=class{constructor(e,t){this.authService=e;this.rechargeService=t}async execute(e,t,r,n="en-US"){let i=oe.safeParse(r);if(!i.success)throw new v(i.error.flatten().fieldErrors);return await this.authService.authenticate(e,t,n),this.rechargeService.createRecharge(i.data,n)}};var q=class{constructor(e,t,r){this.httpClient=e;this.pollingService=t;this.circuitBreaker=r}async execute(e,t,r,n="en-US"){let i=async()=>await this.circuitBreaker.execute(async()=>(await this.httpClient.request({method:"GET",url:`/api/v1/shipments/${e}`,headers:{"Accept-Language":n}})).data);return await this.pollingService.pollUntil(i,m=>m.status===t,{maxAttempts:8,baseDelayMs:2e3,abortSignal:r})}};import{createHmac as Ie,timingSafeEqual as Re}from"crypto";var H=class{constructor(e,t="sha256"){this.secret=e;this.algorithm=t}verifySignature(e,t,r,n){let i=r||this.secret,m=n||this.algorithm,p=Ie(m,i).update(e).digest("hex");if(p.length!==t.length)return!1;try{return Re(Buffer.from(p),Buffer.from(t))}catch{return!1}}async handle(e){console.log(`[Webhook] Received ${e.eventType} at ${e.timestamp}`)}};var L=class{constructor(e=5,t=3e4){this.state="CLOSED";this.failures=0;this.lastFailureTime=null;this.failureThreshold=e,this.resetTimeoutMs=t}async execute(e){if(this.state==="OPEN"){let t=Date.now();if(this.lastFailureTime&&t-this.lastFailureTime>this.resetTimeoutMs)this.state="HALF-OPEN";else throw new Error("Circuit breaker is OPEN")}try{let t=await e();return this.onSuccess(),t}catch(t){throw this.onFailure(),t}}getState(){return this.state}reset(){this.state="CLOSED",this.failures=0,this.lastFailureTime=null}onSuccess(){this.failures=0,this.state==="HALF-OPEN"&&(this.state="CLOSED")}onFailure(){this.failures++,this.lastFailureTime=Date.now(),this.failures>=this.failureThreshold&&(this.state="OPEN")}};import Te from"express-rate-limit";var N=class{constructor(e={}){this.limiter=Te({windowMs:900*1e3,max:100,standardHeaders:!0,legacyHeaders:!1,...e})}async consume(e,t=1){return Promise.resolve({remaining:99,limit:100})}getMiddleware(){return(e,t,r)=>this.limiter(e,t,r)}async close(){}};import{trace as Ee}from"@opentelemetry/api";import{NodeSDK as Pe}from"@opentelemetry/sdk-node";import{getNodeAutoInstrumentations as Ae}from"@opentelemetry/auto-instrumentations-node";import{OTLPTraceExporter as Ce}from"@opentelemetry/exporter-trace-otlp-http";var f=null;function V(o="weshipyou-sdk"){return f||(f=new Pe({serviceName:o,traceExporter:new Ce({url:process.env.OTEL_ENDPOINT||"http://localhost:4318/v1/traces"}),instrumentations:[Ae()]}),f.start(),f)}async function J(){f&&(await f.shutdown(),f=null)}var D=class{constructor(){this.tracer=Ee.getTracer("weshipyou-sdk")}startSpan(e,t){let r=this.tracer.startSpan(e);return t&&Object.entries(t).forEach(([n,i])=>r.setAttribute(n,i)),{setAttribute:(n,i)=>r.setAttribute(n,i),end:()=>r.end()}}async close(){await J()}};import{Router as Oe}from"express";function se(o,e,t,r){let n=Oe();return n.use(o.getMiddleware()),n.use(r),n.post("/shipments/rates",async(i,m,p)=>{let s=e.startSpan("POST /shipments/rates");s.setAttribute("account.id",i.body?.accountId||"unknown");try{let a=await t.rates(i.body);m.json(a)}catch(a){p(a)}finally{s.end()}}),n.post("/shipments",async(i,m,p)=>{let s=e.startSpan("POST /shipments");s.setAttribute("account.id",i.body?.accountId||"unknown");try{let a=await t.createShipment(i.body);m.status(201).json(a)}catch(a){p(a)}finally{s.end()}}),n.get("/shipments/:id",async(i,m,p)=>{let s=e.startSpan("GET /shipments/:id");s.setAttribute("shipment.id",i.params.id);let a=new AbortController;i.on("close",()=>a.abort());try{let c=i.query.targetStatus||"delivered",l=await t.trackShipment(i.params.id,c);m.json(l)}catch(c){p(c)}finally{s.end()}}),n.post("/webhooks",async(i,m,p)=>{let s=e.startSpan("POST /webhooks"),a=i.headers["x-websignature"],c=Array.isArray(a)?a[0]:a;s.setAttribute("webhook.signature",c||"none");try{if(!c)return m.status(401).json({error:"Missing signature header"});await t.webhook(c,JSON.stringify(i.body)),m.status(200).json({received:!0})}catch(l){p(l)}finally{s.end()}}),n}function ae(o){return(e,t,r)=>{let n=e.headers.authorization;if(!n||!n.startsWith("Bearer ")){t.status(401).json({error:"Missing or invalid Authorization header"});return}let i=n.slice(7);if(!i||i.length<10){t.status(401).json({error:"Invalid token format"});return}r()}}var U=class{constructor(){this.totalRequests=0;this.failedRequests=0;this.circuitBreakerTrips=0;this.responseTimes=[];this.maxSamples=1e3}recordRequest(e,t){this.totalRequests++,this.responseTimes.push(e),this.responseTimes.length>this.maxSamples&&this.responseTimes.shift(),t||this.failedRequests++}recordCircuitBreakerTrip(){this.circuitBreakerTrips++}snapshot(){let e=this.responseTimes.length?this.responseTimes.reduce((t,r)=>t+r,0)/this.responseTimes.length:0;return{totalRequests:this.totalRequests,failedRequests:this.failedRequests,circuitBreakerTrips:this.circuitBreakerTrips,averageResponseTimeMs:Math.round(e)}}reset(){this.totalRequests=0,this.failedRequests=0,this.circuitBreakerTrips=0,this.responseTimes=[]}};function ce(o,e){let t=[],r;return o.includes("sandbox")||o.includes("staging")||o.includes("test")?r="test":(o.includes("weshipyou.com/api/v1"),r="production"),e&&(e.startsWith("test_")&&r==="production"&&t.push('API key starts with "test_" but environment is production'),e.startsWith("live_")&&r==="test"&&t.push('API key starts with "live_" but environment is test')),{environment:r,warnings:t}}import{z as B}from"zod";var me=B.object({eventType:B.string().min(1),payload:B.record(B.unknown()).default({}),timestamp:B.string().datetime().optional()});function Me(o,e,t){let{warnings:r}=ce(o);r.forEach(d=>console.warn(d)),t&&(process.env.OTEL_ENDPOINT=t),process.env.OTEL_ENDPOINT&&V();let n=new x,i=new L(5,3e4),m=new A,p=new U,s=new N,a=new D,c=new I(o,n,async()=>{throw new Error("Auto-refresh credentials not configured")}),l=new R(c,n),ee=new $(c),ue=new T(c),pe=new P(c),W,j;if(typeof e=="string")W=e,j=void 0;else if(e)W=e.secret,j=e.algorithm;else throw new Error("Webhook secret is required");let z=new H(W,j),te=new C(l,ee),re=new E(l,ee),le=new O(l,ue),he=new M(l,pe),ne=new q(c,m,i),de=se(s,a,{rates:d=>{let g=d,{username:b,password:w,...G}=g;return te.execute(b,w,G)},createShipment:d=>{let g=d,{username:b,password:w,...G}=g;return re.execute(b,w,G)},trackShipment:(d,g)=>ne.execute(d,g),webhook:async(d,g)=>{if(!z.verifySignature(g,d))throw new Error("Invalid webhook signature");let b=JSON.parse(g),w=me.parse(b);await z.handle(w)}},ae({tokenManager:n}));return{auth:l,httpClient:c,getRates:te,createShipment:re,createSubAccount:le,executeRecharge:he,trackShipment:ne,webhookHandler:z,pollingService:m,circuitBreaker:i,metricsCollector:p,rateLimiter:s,tracer:a,router:de}}import qe from"opossum";function He(o){return new qe(t=>Promise.resolve(t()),o)}var K=class{constructor(e={}){this.breaker=He({timeout:1e4,errorThresholdPercentage:50,resetTimeout:3e4,...e})}async execute(e){return await this.breaker.fire(e)}getState(){let e=this.breaker.stats,t=e.failures+e.successes;return t===0?"CLOSED":e.failures/t*100>=50?"OPEN":"CLOSED"}reset(){this.breaker.reset()}getMetrics(){let e=this.breaker.stats;return{success:e.successes,failure:e.failures,timeout:e.timeouts,rejection:e.rejects}}};import Le from"helmet";var Q=class{getHandler(){return Le({contentSecurityPolicy:{directives:{defaultSrc:["'self'"],scriptSrc:["'self'"],styleSrc:["'self'","'unsafe-inline'"],imgSrc:["'self'","data:","https:"],connectSrc:["'self'","https://weshipyou.com"],fontSrc:["'self'"],objectSrc:["'none'"],upgradeInsecureRequests:[]}},crossOriginEmbedderPolicy:!0,crossOriginOpenerPolicy:!0,crossOriginResourcePolicy:{policy:"same-site"},originAgentCluster:!0,referrerPolicy:{policy:"strict-origin-when-cross-origin"},strictTransportSecurity:{maxAge:31536e3,includeSubDomains:!0},xContentTypeOptions:!0,xDnsPrefetchControl:!0,xDownloadOptions:!0,xFrameOptions:{action:"sameorigin"},xPermittedCrossDomainPolicies:{permittedPolicies:"none"},xXssProtection:!0})}};var X=class{constructor(e,t,r,n=new Date().toISOString(),i=1){this.eventId=e;this.aggregateId=t;this.payload=r;this.timestamp=n;this.version=i;this.type="webhook.received"}},Y=class{constructor(e,t,r,n=new Date().toISOString(),i=1){this.eventId=e;this.aggregateId=t;this.payload=r;this.timestamp=n;this.version=i;this.type="webhook.processed"}};var Z=class{constructor(e,t){this.eventStore=e;this.httpClient=t}async onShipmentCreated(e){await this.eventStore.append(e.aggregateId,[e])}async onShipmentStatusChanged(e){await this.eventStore.append(e.aggregateId,[new _(crypto.randomUUID(),e.aggregateId,{...e.payload},new Date().toISOString(),e.version+1)])}async onWebhookReceived(e){if(e.type==="shipment.status_changed"){let t=new _(crypto.randomUUID(),e.aggregateId,{status:e.payload.status,previousStatus:e.payload.previousStatus,updatedAt:new Date().toISOString()},new Date().toISOString(),e.version+1);await this.eventStore.append(e.aggregateId,[t])}}};var wr="1.0.0";export{T as AccountService,R as AuthService,I as AxiosHttpClient,L as CircuitBreaker,E as CreateShipmentUseCase,O as CreateSubAccountUseCase,M as ExecuteRechargeUseCase,N as ExpressRateLimiter,C as GetRatesUseCase,H as HandleWebhookUseCase,Q as HelmetCspMiddleware,x as InMemoryTokenManager,U as MetricsCollector,P as MobileRechargeService,K as OpossumCircuitBreaker,D as OtelTracer,A as PollingService,wr as SDK_VERSION,Se as ShipmentCreatedEvent,Z as ShipmentReconciliationSaga,$ as ShipmentService,_ as ShipmentStatusChangedEvent,ye as ShipmentUpdatedEvent,fe as SqliteEventStore,q as TrackShipmentUseCase,ge as UpdateShipmentUseCase,Y as WebhookProcessedEvent,X as WebhookReceivedEvent,Me as bootstrap,ve as gracefulShutdown,V as initOpenTelemetry,J as shutdownOpenTelemetry};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|