idosell 0.4.34 → 0.4.41

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.
@@ -8404,7 +8404,7 @@ export type GetProductsResponse = {
8404
8404
  productSizeQuantity: number;
8405
8405
  }[];
8406
8406
  /** @description Size data */
8407
- productSizesStocksLocations: {
8407
+ productSizesStocksLocations?: {
8408
8408
  /** @description Stock ID */
8409
8409
  stockId: number;
8410
8410
  productSizesLocation: {
@@ -8489,11 +8489,11 @@ export type GetProductsResponse = {
8489
8489
  /** @description Gross price */
8490
8490
  productRetailPrice: number;
8491
8491
  /** @description Gross price after promotion. The item is returned when the 'showPromotionsPrices' parameter is specified in the request */
8492
- productPromoRetailPrice: number;
8492
+ productPromoRetailPrice?: number;
8493
8493
  /** @description Wholesale price */
8494
8494
  productWholesalePrice: number;
8495
8495
  /** @description Wholesale price after promotion. The item is returned when the 'showPromotionsPrices' parameter is specified in the request */
8496
- productPromoWholesalePrice: number;
8496
+ productPromoWholesalePrice?: number;
8497
8497
  /** @description Minimal price */
8498
8498
  productMinimalPrice: number;
8499
8499
  /** @description Price for automatic calculations */
@@ -8771,7 +8771,7 @@ export type GetProductsResponse = {
8771
8771
  sizePanelName: string;
8772
8772
  }[];
8773
8773
  /** @description Available sizes of products in a set or collection for marketplaces */
8774
- bundledAvailableSizesInAuctions: {
8774
+ bundledAvailableSizesInAuctions?: {
8775
8775
  /** @description Size identifier */
8776
8776
  sizeId: string;
8777
8777
  /** @description Size name */
@@ -8805,7 +8805,7 @@ export type GetProductsResponse = {
8805
8805
  }[];
8806
8806
  }[];
8807
8807
  /** @description Parameter values */
8808
- parameterValues?: {
8808
+ parameterValues?: null | {
8809
8809
  /** @description Parameter value ID */
8810
8810
  parameterValueId: number;
8811
8811
  /** @description Language data */
@@ -8972,7 +8972,7 @@ export type GetProductsResponse = {
8972
8972
  /** @description Minimum stock level */
8973
8973
  minStockLevel?: number;
8974
8974
  }[];
8975
- } & { resultsLimit: number; };
8975
+ } & { resultsLimit: number };
8976
8976
 
8977
8977
  export type GetOrdersResponse = {
8978
8978
  Results: {
@@ -8997,7 +8997,7 @@ export type GetOrdersResponse = {
8997
8997
  /** @description Customer's company name. */
8998
8998
  clientFirm: string;
8999
8999
  /** @description Product suggestion. */
9000
- clientAdditional: string;
9000
+ clientAdditional?: string;
9001
9001
  /** @description Street and number. */
9002
9002
  clientStreet: string;
9003
9003
  /** @description Customer's postal code. */
@@ -9007,7 +9007,7 @@ export type GetOrdersResponse = {
9007
9007
  /** @description Region name takes priority over clientCountryId. */
9008
9008
  clientCountryName: string;
9009
9009
  /** @description Client NIP verification status */
9010
- clientNipUeVerified: string;
9010
+ clientNipUeVerified: string | null;
9011
9011
  /** @description Country ID in accordance with ISO-3166. */
9012
9012
  clientCountryId: string;
9013
9013
  /** @description Cell phone. */
@@ -9052,9 +9052,9 @@ export type GetOrdersResponse = {
9052
9052
  /** @description Internal Receiving Point Identifier. */
9053
9053
  clientDeliveryAddressPickupPointInternalId: number;
9054
9054
  };
9055
- clientPickupPointAddress: {
9055
+ clientPickupPointAddress?: {
9056
9056
  /** @description Collection point ID. */
9057
- pickupPointId: string;
9057
+ pickupPointId: number;
9058
9058
  /** @description External service collection point ID. */
9059
9059
  externalPickupPointId: string;
9060
9060
  /** @description Town / City. */
@@ -9071,9 +9071,11 @@ export type GetOrdersResponse = {
9071
9071
  longitude: number;
9072
9072
  /** @description Name. */
9073
9073
  name: string;
9074
+ /** @description Country (2 letter country code). */
9075
+ country: string;
9074
9076
  };
9075
9077
  /** @description Buyer's address data. */
9076
- payerAddress: {
9078
+ payerAddress?: {
9077
9079
  /** @description Buyer's address id. */
9078
9080
  payerAddressId: string;
9079
9081
  /** @description Buyer's first name. */
@@ -9137,9 +9139,13 @@ export type GetOrdersResponse = {
9137
9139
  /** @description Order status. Allowed values: "finished_ext" - order status: completed in FA application, "finished" - completed, "new" - not handled, "payment_waiting" - awaiting payment, "delivery_waiting" - awaiting delivery, "on_order" - in progress, "packed" - being picked, "packed_fulfillment" - being picked - fulfilment, "packed_ready" - packed, "ready" - ready, "wait_for_dispatch" - awaiting dispatch date, "suspended" - on hold, "joined" - merged, "missing" - missing, "lost" - lost, "false" - false, "canceled" - Customer canceled. */
9138
9140
  orderStatus: string;
9139
9141
  /** @description Order status id */
9140
- orderStatusId: string;
9142
+ orderStatusId?: number;
9141
9143
  /** @description Date of change of status to the currently set status in YYYY-MM-DD HH:MM:SS format. */
9142
9144
  orderStatusChangeDate: string;
9145
+ /** @description Split payment MPP marking */
9146
+ splitPayment: boolean;
9147
+ /** @description Transaction type. */
9148
+ transactionType: null |"national" | "oss" | "export" | "intra";
9143
9149
  /** @example dropshippingOrderStatus */
9144
9150
  dropshippingOrderStatus: string;
9145
9151
  /** @description Type of order confirmation. Confirmations listing: "none" - order unconfirmed , "email" - order confirmed by e-mail, "phone_client" - order confirmed by phone call made by client, "phone_service" - order confirmed by phone call made by staff, "postauction" - order confirmed by auction return page, "willingness" - confirmed by willingness to buy letter, "auctionfod" - confirmed by after-sales form Allegro. */
@@ -9147,11 +9153,11 @@ export type GetOrdersResponse = {
9147
9153
  /** @description Date of order placing in YYYY-MM-DD HH:MM:SS format. */
9148
9154
  orderAddDate: string;
9149
9155
  /** @description Date of order sending in YYYY-MM-DD HH:MM:SS format. */
9150
- orderDispatchDate: string | null;
9156
+ orderDispatchDate: 0 | string | null;
9151
9157
  /** @example receivedDate */
9152
9158
  receivedDate: string;
9153
9159
  /** @description Order handling time in seconds. */
9154
- orderPrepareTime: string;
9160
+ orderPrepareTime: number | null;
9155
9161
  /** @description Customer comments on order. */
9156
9162
  clientNoteToOrder: string;
9157
9163
  /** @description Customer remarks for courier. */
@@ -9171,7 +9177,7 @@ export type GetOrdersResponse = {
9171
9177
  /** @description Currency average rate set for order (by default, an average rate of order adding date, if it wasn't manually changed). */
9172
9178
  orderCurrencyValue: number;
9173
9179
  /** @description Currency scaler. */
9174
- orderCurrencyScale: number;
9180
+ orderCurrencyScale?: number;
9175
9181
  /** @description Panel billing currency exchange rate in relation to billing currency in the shop . */
9176
9182
  billingCurrencyRate: number;
9177
9183
  /** @description Products cost. */
@@ -9204,7 +9210,7 @@ export type GetOrdersResponse = {
9204
9210
  };
9205
9211
  /** @description Order currency squaring method. "gross" - calculated in gross prices, "net" - squared in net prices. */
9206
9212
  orderWorthCalculateType: "gross" | "net";
9207
- /** @description Information if the VAT for the current order was calculated: "y" - yes, "n" - no. */
9213
+ /** @description Information if the VAT for the current order was calculated: "y" - yes, "n" - no, "p" – requires approval by the store staff. */
9208
9214
  orderVatExists: string;
9209
9215
  };
9210
9216
  /** @description Information about prepayment for the order. */
@@ -9236,9 +9242,9 @@ export type GetOrdersResponse = {
9236
9242
  /** @description Currency ID */
9237
9243
  currencyId: string;
9238
9244
  /** @description Number of voucher used in a payment. */
9239
- voucherNumber: string;
9245
+ voucherNumber?: string;
9240
9246
  /** @description Number of gift card used in a payment. */
9241
- giftCardNumber: string;
9247
+ giftCardNumber?: string;
9242
9248
  }[];
9243
9249
  /** @description Order source data. */
9244
9250
  orderSourceResults: {
@@ -9247,7 +9253,7 @@ export type GetOrdersResponse = {
9247
9253
  /** @description Shop Id */
9248
9254
  shopId: number;
9249
9255
  /** @description Auction site order comes from. Auction sites listing: "allegro" - Allegro.pl, "testwebapi" - Allegro.pl test site, "ebay" - eBay. */
9250
- auctionsServiceName: string;
9256
+ auctionsServiceName: string | null;
9251
9257
  /** @description Detailed information on order source. */
9252
9258
  orderSourceDetails: {
9253
9259
  /** @description order source type - possible values:. "self_added" - Orders from panel, "shop" - Orders from shop, "search_engine" - Orders from search engines, "auction" - Orders from auctions, "advertisement_campaign" - Advertisement campaigns, "price_comparer" - Price comparison sites, "affiliate_program" - Affiliate programme, "api" - Order from API, "eletronic_offer" - Order from ODT price lst, "cpa" - Order from CPA program, "refferer_site" - Order from reference sites, "pos" - Orders from POS, "marketplace" - Order from the Marketplace, "iai_ads" - Orders from IAI Ads */
@@ -9259,11 +9265,11 @@ export type GetOrdersResponse = {
9259
9265
  /** @description Numerical ID of order source. */
9260
9266
  orderSourceId: number;
9261
9267
  /** @example 1 */
9262
- entryProductIdBeforeOrder: number;
9268
+ entryProductIdBeforeOrder?: number;
9263
9269
  /** @example sourcePageUrl */
9264
- sourcePageUrl: string;
9270
+ sourcePageUrl?: string;
9265
9271
  /** @description The order ID of the external service */
9266
- orderExternalId: string;
9272
+ orderExternalId: string | null;
9267
9273
  /** @description Order from the InPost Fresh marketplace */
9268
9274
  fresh: "y" | "n";
9269
9275
  /** @description Order supported by InPost fulfillment */
@@ -9285,16 +9291,16 @@ export type GetOrdersResponse = {
9285
9291
  /** @description Data of auction, order comes from (only if it comes from auction). */
9286
9292
  auctionInfo: {
9287
9293
  /** @description Account ID on auction site. */
9288
- auctionClientId: string;
9294
+ auctionClientId?: string;
9289
9295
  /** @description Account login on auction site. */
9290
- auctionClientLogin: string;
9296
+ auctionClientLogin?: string;
9291
9297
  /** @description #!TablicaNumerowAukcjiDoZamowienia!#. */
9292
- auctionItemsIds: {
9298
+ auctionItemsIds?: {
9293
9299
  /** @description Auction number. */
9294
9300
  auctionItemId: string;
9295
9301
  }[];
9296
9302
  /** @description The customer's email address at the auction service. */
9297
- auctionClientEmail: string;
9303
+ auctionClientEmail?: string;
9298
9304
  };
9299
9305
  /** @description Consignment data. */
9300
9306
  dispatch: {
@@ -9324,7 +9330,7 @@ export type GetOrdersResponse = {
9324
9330
  /** @description External product system code */
9325
9331
  productCode: string;
9326
9332
  /** @description Name of the parameter value, e.g. orange, green, red */
9327
- versionName: string;
9333
+ versionName?: string;
9328
9334
  /** @description Size identifier */
9329
9335
  sizeId: string;
9330
9336
  /** @description Size name */
@@ -9334,7 +9340,7 @@ export type GetOrdersResponse = {
9334
9340
  /** @description Stock ID */
9335
9341
  stockId: number;
9336
9342
  /** @description Serial number of the product. */
9337
- productSerialNumber: string;
9343
+ productSerialNumber?: string;
9338
9344
  /** @description Product quantity. */
9339
9345
  productQuantity: number;
9340
9346
  /** @description Weight. */
@@ -9342,7 +9348,7 @@ export type GetOrdersResponse = {
9342
9348
  /** @description Value of VAT */
9343
9349
  productVat: number;
9344
9350
  /** @description Is product VAT free Allowed values "y" - yes, "n" - no. */
9345
- productVatFree: string;
9351
+ productVatFree?: string;
9346
9352
  /** @description Gross price of the product in the currency of the administration panel. */
9347
9353
  productPanelPrice: number;
9348
9354
  /** @description Net price of the product in the currency of the administration panel. */
@@ -9356,7 +9362,7 @@ export type GetOrdersResponse = {
9356
9362
  /** @description Product net price of order in shop account currency. */
9357
9363
  productOrderPriceNetBaseCurrency: number;
9358
9364
  /** @description List of product suggestions . */
9359
- orderAdditionalList: {
9365
+ orderAdditionalList?: {
9360
9366
  /** @description Product suggestion. */
9361
9367
  orderAdditional: {
9362
9368
  /** @description Name of suggestion. */
@@ -9368,13 +9374,13 @@ export type GetOrdersResponse = {
9368
9374
  /** @description Client's remarks on product. */
9369
9375
  remarksToProduct: string;
9370
9376
  /** @description Label for grouping products. */
9371
- label: string;
9377
+ label: string | null;
9372
9378
  /** @description Product selling mode. Available values: "money", "gift", "points". */
9373
9379
  orderSalesMode: "money" | "gift" | "points";
9374
9380
  /** @description A set's ID. */
9375
9381
  bundleId: number;
9376
9382
  /** @description Serial numbers. */
9377
- productSerialNumbers: string;
9383
+ productSerialNumbers: string | null;
9378
9384
  /** @description Additional information. */
9379
9385
  productOrderAdditional: string;
9380
9386
  /** @description Item in basket. */
@@ -9382,7 +9388,7 @@ export type GetOrdersResponse = {
9382
9388
  /** @description price information. */
9383
9389
  productPriceLog: string;
9384
9390
  /** @description Information about the selected parameters in the configurator. */
9385
- priceFormulaParameters: {
9391
+ priceFormulaParameters?: {
9386
9392
  /** @description Parameter ID */
9387
9393
  parameterId: string;
9388
9394
  /** @description Parameter name. */
@@ -9409,7 +9415,7 @@ export type GetOrdersResponse = {
9409
9415
  /** @description Note to the order. */
9410
9416
  orderNote: string;
9411
9417
  /** @description Information on used discount code. */
9412
- discountCode: {
9418
+ discountCode?: {
9413
9419
  /** @description Campaign ID. */
9414
9420
  campaignId: string;
9415
9421
  /** @description Name of code. */
@@ -9418,14 +9424,14 @@ export type GetOrdersResponse = {
9418
9424
  discountCodeValue: string;
9419
9425
  };
9420
9426
  /** @description Discount card */
9421
- discountCard: {
9427
+ discountCard?: {
9422
9428
  /** @description Name of card */
9423
9429
  discountCardName: string;
9424
9430
  };
9425
9431
  /** @description Order handler. */
9426
9432
  orderOperatorLogin: string;
9427
9433
  /** @description Order picker. */
9428
- orderPackingPersonLogin: string;
9434
+ orderPackingPersonLogin: string | null;
9429
9435
  /** @description Sale date. ISO 8602 format. */
9430
9436
  purchaseDate: string;
9431
9437
  /** @description Modification date in YYYY-MM-DD HH:MM:SS format . */
@@ -9440,14 +9446,9 @@ export type GetOrdersResponse = {
9440
9446
  verified: boolean;
9441
9447
  };
9442
9448
  /** @description Information on error that occurred during gate call. */
9443
- errors: {
9444
- /** @description Error code. */
9445
- faultCode: number;
9446
- /** @description Error description. */
9447
- faultString: string;
9448
- }[];
9449
+ errors: FaultCodeString[];
9449
9450
  }[];
9450
- };
9451
+ } & { resultsNumberAll: number };
9451
9452
 
9452
9453
  type PromotionErrorEntry = {
9453
9454
  /** @description Error code. */
@@ -9583,4 +9584,30 @@ export type GetRegulationsHistoryResponse = {
9583
9584
  pagination: PagedResponse;
9584
9585
  };
9585
9586
 
9587
+ export type GetProductsAttachmentsGetContentResponse = {
9588
+ /** @description Product attachment content */
9589
+ data: {
9590
+ /** @description Product attachment content */
9591
+ attachmentContent: string | null;
9592
+ /** @description Product attachment content representation
9593
+ |null} */
9594
+ attachmentContentRepresentation: "base64" | "url" | null;
9595
+ /** @description Product attachment file extension */
9596
+ attachmentContentFileExtension: string | null;
9597
+ };
9598
+ isError: boolean;
9599
+ errors: {
9600
+ /** @description Error code. */
9601
+ code: string;
9602
+ /** @description Field associated with the error. */
9603
+ field: string | null;
9604
+ /** @description Error description. */
9605
+ message: string | null;
9606
+ /** @description Value associated with the error. */
9607
+ value: string | null;
9608
+ /** @description Unique identifier of the error (for support). */
9609
+ uid: string | null;
9610
+ }[];
9611
+ };
9612
+
9586
9613
  export { };
package/dist/utils.d.ts CHANGED
@@ -11,6 +11,16 @@ type GetLangDataFunction = <T extends {
11
11
  langId: string;
12
12
  }>(_array: T[], _langId?: string) => T | undefined;
13
13
  type ClearParametersLangDataFunction = (_products: SearchProductsResponse['results'], _langId?: string) => SearchProductsResponse['results'];
14
+ type MappedParameterValue = {
15
+ valueId: number;
16
+ value: string;
17
+ };
18
+ type MappedParameter = {
19
+ id: number;
20
+ name: string;
21
+ values: MappedParameterValue[];
22
+ };
23
+ type MapProductParametersFunction = (_product: IdosellProduct, _langId?: string) => MappedParameter[];
14
24
  declare const _default: {
15
25
  /** @description The method allows you to build an IAI code from the product ID and size ID. */
16
26
  getIaiCode: GetIaICodeFunction;
@@ -30,5 +40,7 @@ declare const _default: {
30
40
  clearParametersLangData: ClearParametersLangDataFunction;
31
41
  /** @description Removes attachments to RMA that are returned by default, helps to reduce data if serialized or forwarded */
32
42
  removeRmaAttachments: (rmaResponse: GetRmaResponse) => GetRmaResponse;
43
+ /** @description Maps product parameters to a simplified structure for a given language. Skips parameters with no values. */
44
+ mapProductParameters: MapProductParametersFunction;
33
45
  };
34
46
  export default _default;
package/dist/utils.js CHANGED
@@ -269,6 +269,23 @@ const removeRmaAttachments = (rmaResponse) => {
269
269
  }
270
270
  return rmaResponse;
271
271
  };
272
+ const mapProductParameters = (product, langId = 'pol') => {
273
+ if (!product.productParameters)
274
+ return [];
275
+ return product.productParameters.reduce((acc, param) => {
276
+ if (!param.parameterValues?.length)
277
+ return acc;
278
+ const name = param.parameterDescriptionsLangData.find((l) => l.langId === langId)?.parameterName ?? '';
279
+ const values = param.parameterValues.reduce((valAcc, pv) => {
280
+ const value = pv.parameterValueDescriptionsLangData.find((l) => l.langId === langId)
281
+ ?.parameterValueName ?? '';
282
+ valAcc.push({ valueId: pv.parameterValueId, value });
283
+ return valAcc;
284
+ }, []);
285
+ acc.push({ id: param.parameterId, name, values });
286
+ return acc;
287
+ }, []);
288
+ };
272
289
  export default {
273
290
  /** @description The method allows you to build an IAI code from the product ID and size ID. */
274
291
  getIaiCode,
@@ -287,5 +304,7 @@ export default {
287
304
  /** @description Modifies product response by removing all parameter names nad values that are not in selected langId */
288
305
  clearParametersLangData,
289
306
  /** @description Removes attachments to RMA that are returned by default, helps to reduce data if serialized or forwarded */
290
- removeRmaAttachments
307
+ removeRmaAttachments,
308
+ /** @description Maps product parameters to a simplified structure for a given language. Skips parameters with no values. */
309
+ mapProductParameters,
291
310
  };
@@ -0,0 +1,124 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import { normalizeIaiRequest, WebhookValidationError } from "./webhooks.normalizer.js";
3
+ // ─── Runtime constants ────────────────────────────────────────────────────────
4
+ export const WEBHOOK_OBJECT_TYPE = {
5
+ CLIENT: "client",
6
+ PRODUCT: "product",
7
+ ORDER: "order",
8
+ RETURN: "return",
9
+ RMA: "rma",
10
+ };
11
+ export const WEBHOOK_EVENT_TYPE = {
12
+ CLIENT_CREATED: "clientCreated",
13
+ CLIENT_UPDATED: "clientUpdated",
14
+ PRODUCT_CREATED: "productCreated",
15
+ PRODUCT_UPDATED: "productUpdated",
16
+ PRODUCT_PRICE_UPDATED: "productPriceUpdated",
17
+ PRODUCT_STOCK_UPDATED: "productStockUpdated",
18
+ PRODUCT_DISPOSITION_UPDATED: "productDispositionUpdated",
19
+ ORDER_CREATED: "orderCreated",
20
+ ORDER_UPDATED: "orderUpdated",
21
+ ORDER_PAID: "orderPaid",
22
+ ORDER_STATUS_UPDATED: "orderStatusUpdated",
23
+ ORDER_PACKAGE_CREATED: "orderPackageCreated",
24
+ ORDER_FILES_CREATED: "orderFilesCreated",
25
+ ORDER_SALE_DOCUMENT_CREATED: "orderSaleDocumentCreated",
26
+ ORDER_SALE_DOCUMENT_UPDATED: "orderSaleDocumentUpdated",
27
+ ORDER_SENT: "orderSent",
28
+ ORDER_DELIVERED: "orderDelivered",
29
+ ORDER_CANCELED: "orderCanceled",
30
+ RETURN_CREATED: "returnCreated",
31
+ RETURN_UPDATED: "returnUpdated",
32
+ RETURN_FUNDS_CONFIRMED: "returnFundsConfirmed",
33
+ RETURN_PACKAGE_CREATED: "returnPackageCreated",
34
+ RETURN_CONFIRMED: "returnConfirmed",
35
+ RETURN_CANCELED: "returnCanceled",
36
+ RMA_CREATED: "rmaCreated",
37
+ RMA_UPDATED: "rmaUpdated",
38
+ RMA_PACKAGE_CREATED: "rmaPackageCreated",
39
+ RMA_APPROVED: "rmaApproved",
40
+ RMA_REJECTED: "rmaRejected",
41
+ };
42
+ // ─── Runtime event → object map ───────────────────────────────────────────────
43
+ const EVENT_OBJECT_MAP = {
44
+ clientCreated: "client",
45
+ clientUpdated: "client",
46
+ productCreated: "product",
47
+ productUpdated: "product",
48
+ productPriceUpdated: "product",
49
+ productStockUpdated: "product",
50
+ productDispositionUpdated: "product",
51
+ orderCreated: "order",
52
+ orderUpdated: "order",
53
+ orderPaid: "order",
54
+ orderStatusUpdated: "order",
55
+ orderPackageCreated: "order",
56
+ orderFilesCreated: "order",
57
+ orderSaleDocumentCreated: "order",
58
+ orderSaleDocumentUpdated: "order",
59
+ orderSent: "order",
60
+ orderDelivered: "order",
61
+ orderCanceled: "order",
62
+ returnCreated: "return",
63
+ returnUpdated: "return",
64
+ returnFundsConfirmed: "return",
65
+ returnPackageCreated: "return",
66
+ returnConfirmed: "return",
67
+ returnCanceled: "return",
68
+ rmaCreated: "rma",
69
+ rmaUpdated: "rma",
70
+ rmaPackageCreated: "rma",
71
+ rmaApproved: "rma",
72
+ rmaRejected: "rma",
73
+ };
74
+ export class WebhookChain {
75
+ slots = [];
76
+ validator = null;
77
+ validateHeaders(validator) {
78
+ this.validator = validator;
79
+ return this;
80
+ }
81
+ on(eventType, handler) {
82
+ this.slots.push({
83
+ eventType,
84
+ fn: handler,
85
+ });
86
+ return this;
87
+ }
88
+ async handle(req) {
89
+ const { headers, body } = await normalizeIaiRequest(req);
90
+ if (this.validator !== null) {
91
+ let valid = false;
92
+ try {
93
+ valid = await this.validator(headers);
94
+ }
95
+ catch {
96
+ valid = false;
97
+ }
98
+ if (!valid) {
99
+ return { matched: false, eventType: null, reason: "validation_failed" };
100
+ }
101
+ }
102
+ const incomingEvent = headers.eventType;
103
+ const expectedObject = EVENT_OBJECT_MAP[incomingEvent];
104
+ if (expectedObject !== undefined && headers.objectType !== expectedObject) {
105
+ throw new WebhookValidationError(`eventType "${incomingEvent}" expects objectType "${expectedObject}", got "${headers.objectType}"`, "x-iai-object-type");
106
+ }
107
+ const slot = this.slots.find(s => s.eventType === incomingEvent);
108
+ if (!slot) {
109
+ return { matched: false, eventType: incomingEvent };
110
+ }
111
+ await slot.fn({ headers, body });
112
+ return { matched: true, eventType: slot.eventType };
113
+ }
114
+ }
115
+ // ─── Entry point ──────────────────────────────────────────────────────────────
116
+ export const webhooks = {
117
+ validateHeaders(validator) {
118
+ return new WebhookChain().validateHeaders(validator);
119
+ },
120
+ on(eventType, handler) {
121
+ return new WebhookChain().on(eventType, handler);
122
+ },
123
+ };
124
+ export default webhooks;
@@ -0,0 +1,140 @@
1
+ // ─── Errors ──────────────────────────────────────────────────────────────────
2
+ export class WebhookValidationError extends Error {
3
+ field;
4
+ constructor(message, field) {
5
+ super(message);
6
+ this.field = field;
7
+ this.name = `WebhookValidationError ${field}`;
8
+ }
9
+ }
10
+ // ─── Internal helpers ────────────────────────────────────────────────────────
11
+ function requireHeader(headers, key) {
12
+ const value = headers[key];
13
+ if (!value || value.trim() === "") {
14
+ throw new WebhookValidationError(`Missing or empty required header: ${key}`, key);
15
+ }
16
+ return value.trim();
17
+ }
18
+ function parseIntHeader(headers, key) {
19
+ const raw = requireHeader(headers, key);
20
+ const n = Number(raw);
21
+ if (!Number.isInteger(n) || n < 0) {
22
+ throw new WebhookValidationError(`Header ${key} must be a positive integer, got: "${raw}"`, key);
23
+ }
24
+ return n;
25
+ }
26
+ function parseDateHeader(headers, key) {
27
+ const raw = requireHeader(headers, key);
28
+ const date = new Date(raw);
29
+ if (isNaN(date.getTime())) {
30
+ throw new WebhookValidationError(`Header ${key} is not a valid date string, got: "${raw}"`, key);
31
+ }
32
+ return date;
33
+ }
34
+ function parseAuthHeader(headers) {
35
+ const raw = requireHeader(headers, "authorization");
36
+ const [scheme, token] = raw.split(" ");
37
+ if (scheme?.toLowerCase() !== "bearer" || !token) {
38
+ throw new WebhookValidationError(`Authorization header must use Bearer scheme, got: "${raw}"`, "authorization");
39
+ }
40
+ return token;
41
+ }
42
+ function extractHeaders(raw) {
43
+ return {
44
+ token: parseAuthHeader(raw),
45
+ apiVersion: parseIntHeader(raw, "x-iai-api-version"),
46
+ eventTime: parseDateHeader(raw, "x-iai-event-time"),
47
+ eventType: requireHeader(raw, "x-iai-event-type"),
48
+ eventUid: requireHeader(raw, "x-iai-event-uid"),
49
+ objectType: requireHeader(raw, "x-iai-object-type"),
50
+ panelId: parseIntHeader(raw, "x-iai-panel-id"),
51
+ signature: requireHeader(raw, "x-iai-signature"),
52
+ webhookTime: parseDateHeader(raw, "x-iai-webhook-time"),
53
+ };
54
+ }
55
+ // ─── Type guard ──────────────────────────────────────────────────────────────
56
+ function isWebRequest(req) {
57
+ return typeof req.json === "function";
58
+ }
59
+ // ─── Raw headers flattening ──────────────────────────────────────────────────
60
+ function flattenNodeHeaders(headers) {
61
+ const out = {};
62
+ for (const [key, value] of Object.entries(headers)) {
63
+ if (value !== undefined) {
64
+ out[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
65
+ }
66
+ }
67
+ return out;
68
+ }
69
+ function flattenWebHeaders(headers) {
70
+ const out = {};
71
+ headers.forEach((value, key) => { out[key.toLowerCase()] = value; });
72
+ return out;
73
+ }
74
+ function readNodeStream(req) {
75
+ return new Promise((resolve, reject) => {
76
+ const chunks = [];
77
+ req.on("data", (chunk) => chunks.push(chunk));
78
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
79
+ req.on("error", reject);
80
+ });
81
+ }
82
+ // ─── Main normalizer ─────────────────────────────────────────────────────────
83
+ /**
84
+ * Parses and validates an incoming IAI webhook request from any of:
85
+ * - Express (pass req directly — do NOT attach express.json())
86
+ * - Next.js Pages (disable bodyParser in route config)
87
+ * - Next.js App (Web Fetch API Request)
88
+ * - SvelteKit (Web Fetch API Request — pass event.request)
89
+ *
90
+ * Throws WebhookValidationError for any missing/malformed header or empty body.
91
+ *
92
+ * @example Express
93
+ * app.post('/webhook', async (req, res) => {
94
+ * const { headers, body } = await normalizeIaiRequest(req);
95
+ * });
96
+ *
97
+ * @example SvelteKit
98
+ * export async function POST({ request }) {
99
+ * const { headers, body } = await normalizeIaiRequest(request);
100
+ * }
101
+ *
102
+ * @example Next.js App Router
103
+ * export async function POST(request: Request) {
104
+ * const { headers, body } = await normalizeIaiRequest(request);
105
+ * }
106
+ *
107
+ * @example Next.js Pages Router (add export const config = { api: { bodyParser: false } })
108
+ * export default async function handler(req, res) {
109
+ * const { headers, body } = await normalizeIaiRequest(req);
110
+ * }
111
+ */
112
+ export async function normalizeIaiRequest(req) {
113
+ let flatHeaders;
114
+ let rawBody;
115
+ if (isWebRequest(req)) {
116
+ flatHeaders = flattenWebHeaders(req.headers);
117
+ rawBody = await req.text();
118
+ }
119
+ else {
120
+ flatHeaders = flattenNodeHeaders(req.headers);
121
+ rawBody = req.body !== undefined
122
+ ? JSON.stringify(req.body)
123
+ : await readNodeStream(req);
124
+ }
125
+ if (!rawBody || rawBody.trim() === "") {
126
+ throw new WebhookValidationError("Request body is empty", "body");
127
+ }
128
+ let body;
129
+ try {
130
+ body = JSON.parse(rawBody);
131
+ }
132
+ catch {
133
+ throw new WebhookValidationError("Request body is not valid JSON", "body");
134
+ }
135
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
136
+ throw new WebhookValidationError("Request body must be a JSON object", "body");
137
+ }
138
+ const headers = extractHeaders(flatHeaders);
139
+ return { headers, body };
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "idosell",
3
- "version": "0.4.34",
3
+ "version": "0.4.41",
4
4
  "description": "Idosell 3 REST connector",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/gateways.d.ts",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "homepage": "https://idosell-converter.vercel.app",
25
25
  "dependencies": {
26
- "axios": "^1.13.2"
26
+ "axios": "^1.17.0"
27
27
  },
28
28
  "type": "module"
29
29
  }