@jskit-ai/connectors-catalog 0.1.67 → 0.2.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +1722 -0
  2. package/README.md +11 -0
  3. package/locales/en.json +3139 -0
  4. package/package.json +7 -6
  5. package/src/server/ai.js +4 -4
  6. package/src/server/amazon-redshift.js +5 -5
  7. package/src/server/apify.js +1 -1
  8. package/src/server/apollo-io.js +1 -1
  9. package/src/server/asana.js +3 -3
  10. package/src/server/ashby.js +3 -3
  11. package/src/server/attention.js +2 -2
  12. package/src/server/aws-athena.js +3 -3
  13. package/src/server/aws-s3.js +1 -1
  14. package/src/server/awsTransport.js +9 -9
  15. package/src/server/brevo.js +3 -3
  16. package/src/server/calendly.js +2 -2
  17. package/src/server/chargebee.js +1 -1
  18. package/src/server/databricks.js +2 -2
  19. package/src/server/dbt-semantic-layer.js +8 -8
  20. package/src/server/elevenlabs.js +4 -4
  21. package/src/server/firebase-cloud-messaging.js +8 -8
  22. package/src/server/firecrawl.js +2 -2
  23. package/src/server/gatewayapi.js +4 -4
  24. package/src/server/github-api.js +3 -3
  25. package/src/server/gitlab-api.js +1 -1
  26. package/src/server/gmail.js +1 -1
  27. package/src/server/gong.js +4 -4
  28. package/src/server/google-ads-search.js +16 -16
  29. package/src/server/google-ads.js +7 -7
  30. package/src/server/google-drive.js +8 -6
  31. package/src/server/google-maps-platform.js +5 -5
  32. package/src/server/google-search-console.js +1 -1
  33. package/src/server/google-sheets.js +3 -2
  34. package/src/server/google.js +3 -2
  35. package/src/server/granola.js +5 -5
  36. package/src/server/hubspot.js +5 -3
  37. package/src/server/inngest.js +4 -4
  38. package/src/server/klipy.js +1 -1
  39. package/src/server/lightspeed.js +4 -4
  40. package/src/server/linear.js +2 -2
  41. package/src/server/linkedin.js +6 -6
  42. package/src/server/mapbox.js +1 -1
  43. package/src/server/mcpRegistration.js +3 -2
  44. package/src/server/mcpTransport.js +1 -1
  45. package/src/server/microsoft-fabric.js +3 -3
  46. package/src/server/microsoft-onedrive.js +1 -1
  47. package/src/server/microsoft-onenote.js +1 -1
  48. package/src/server/microsoft-outlook.js +1 -1
  49. package/src/server/microsoft.js +4 -3
  50. package/src/server/n8n.js +4 -4
  51. package/src/server/oura.js +1 -1
  52. package/src/server/paddle.js +1 -1
  53. package/src/server/pipedrive.js +4 -4
  54. package/src/server/posthog.js +3 -3
  55. package/src/server/salesforce.js +8 -8
  56. package/src/server/semrush.js +25 -25
  57. package/src/server/shopify.js +6 -6
  58. package/src/server/slack.js +10 -10
  59. package/src/server/snowflake.js +5 -5
  60. package/src/server/tally.js +1 -1
  61. package/src/server/telegram.js +1 -1
  62. package/src/server/tiktok.js +14 -14
  63. package/src/server/twilio.js +3 -3
  64. package/src/server/twitch.js +22 -21
  65. package/src/server/wave.js +34 -34
  66. package/src/server/wix.js +73 -73
  67. package/src/server/wiz.js +18 -18
  68. package/src/server/woocommerce.js +23 -23
  69. package/src/server/wordpress-com.js +3 -3
  70. package/src/server/wordpress-self-hosted.js +2 -2
  71. package/src/server/workday.js +3 -3
  72. package/src/server/x-twitter.js +6 -6
  73. package/src/server/xero.js +15 -15
  74. package/src/server/zoho-books.js +8 -8
  75. package/src/server/zoho-crm.js +5 -5
  76. package/src/server/zohoOAuth.js +1 -1
  77. package/src/shared/ai.js +24 -13
  78. package/src/shared/algolia.js +4 -4
  79. package/src/shared/amazon-redshift.js +16 -16
  80. package/src/shared/amplitude.js +6 -6
  81. package/src/shared/atlassian.js +19 -19
  82. package/src/shared/aws.js +18 -18
  83. package/src/shared/canva.js +18 -18
  84. package/src/shared/chargebee.js +3 -3
  85. package/src/shared/clickhouse.js +9 -9
  86. package/src/shared/confidence.js +6 -6
  87. package/src/shared/databricks.js +8 -8
  88. package/src/shared/dbt-semantic-layer.js +7 -7
  89. package/src/shared/english-ai.js +6 -0
  90. package/src/shared/english-definitions.js +34 -0
  91. package/src/shared/english-texts.js +7 -0
  92. package/src/shared/figma.js +2 -2
  93. package/src/shared/firebase-cloud-messaging.js +11 -11
  94. package/src/shared/firecrawl.js +1 -1
  95. package/src/shared/gemini-enterprise.js +7 -7
  96. package/src/shared/gong.js +5 -5
  97. package/src/shared/google-ads.js +5 -5
  98. package/src/shared/google-maps-platform.js +7 -7
  99. package/src/shared/google.js +40 -40
  100. package/src/shared/granola.js +5 -5
  101. package/src/shared/hex.js +7 -7
  102. package/src/shared/inngest.js +8 -8
  103. package/src/shared/lightspeed.js +52 -52
  104. package/src/shared/linkedin.js +6 -6
  105. package/src/shared/logo-dev.js +3 -3
  106. package/src/shared/mapbox.js +6 -6
  107. package/src/shared/mcp.js +9 -9
  108. package/src/shared/microsoft-fabric.js +9 -9
  109. package/src/shared/microsoft.js +52 -52
  110. package/src/shared/miro.js +5 -5
  111. package/src/shared/oura.js +5 -5
  112. package/src/shared/posthog.js +9 -9
  113. package/src/shared/prestashop.js +5 -5
  114. package/src/shared/resend.js +1 -1
  115. package/src/shared/salesforce.js +8 -8
  116. package/src/shared/semrush.js +6 -6
  117. package/src/shared/shopify.js +26 -26
  118. package/src/shared/slack.js +61 -61
  119. package/src/shared/snowflake.js +15 -15
  120. package/src/shared/tiktok.js +7 -7
  121. package/src/shared/tokens.js +126 -126
  122. package/src/shared/twilio.js +7 -7
  123. package/src/shared/twitch.js +24 -24
  124. package/src/shared/wave.js +33 -33
  125. package/src/shared/wix.js +5 -5
  126. package/src/shared/wiz.js +14 -14
  127. package/src/shared/wordpress-com.js +9 -9
  128. package/src/shared/wordpress.js +11 -11
  129. package/src/shared/workday.js +8 -8
  130. package/src/shared/x-twitter.js +5 -5
  131. package/src/shared/xero.js +25 -25
  132. package/src/shared/zoho-books.js +10 -10
  133. package/src/shared/zoho-crm.js +16 -16
  134. package/src/shared/zoho-regions.js +8 -8
  135. package/test/englishDefinitions.test.js +37 -0
  136. package/test/i18nCatalog.test.js +16 -0
  137. package/test/shopify.test.js +3 -1
package/src/server/wix.js CHANGED
@@ -15,8 +15,8 @@ function serializeNativeJson(value) {
15
15
  const guid = (value) => typeof value === "string" && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/iu.test(value);
16
16
  // Results are checked against the operation's own request; a different record is reported with what Wix got wrong.
17
17
  const requestPath = (request, index) => decodeURIComponent(new URL(request.url).pathname.split("/").at(index));
18
- function sameRecord(matches, message) {
19
- if (!matches) throw new ConnectorError("connector_response_invalid", message, { statusCode: 502 });
18
+ function sameRecord(matches, message, codes) {
19
+ if (!matches) throw new ConnectorError("connector_response_invalid", message, { statusCode: 502, ...codes });
20
20
  return true;
21
21
  }
22
22
  const cursor = (value) => typeof value === "string" && value.length > 0 && value.length <= 16000 && !/[\p{Cc}]/u.test(value);
@@ -77,14 +77,14 @@ function contactOperation(action) {
77
77
  const { contactId, ...body } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
78
78
  if (action !== "get" && (!Object.keys(body.info).length || (action === "create" &&
79
79
  !(body.info.name?.first || body.info.name?.last || body.info.emails?.items?.length || body.info.phones?.items?.length)))) {
80
- throw new ConnectorError("connector_input_invalid", "Provide contact details; creation requires a name, email or phone.", { statusCode: 422 });
80
+ throw new ConnectorError("connector_input_invalid", "Provide contact details; creation requires a name, email or phone.", { statusCode: 422, messageCode: "connectors.wix.provideContactDetailsCreationRequires" });
81
81
  }
82
82
  return { method: action === "get" ? "GET" : action === "create" ? "POST" : "PATCH",
83
83
  url: `https://www.wixapis.com/contacts/v4/contacts${contactId ? `/${contactId}` : ""}`,
84
84
  ...(action === "get" ? {} : { body }) };
85
85
  },
86
86
  validateResult: (value, request) => validContact(value?.contact) && (action === "create" ||
87
- sameRecord(value.contact.id === requestPath(request, -1), "Wix returned a different contact. Check the operation outcome before retrying."))
87
+ sameRecord(value.contact.id === requestPath(request, -1), "Wix returned a different contact. Check the operation outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentContactCheckOperation" }))
88
88
  };
89
89
  }
90
90
  const servicePagingSchema = createSchema({ limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 } });
@@ -125,8 +125,8 @@ function requestedAvailability(value, requested, events) {
125
125
  const slots = value.timeSlots ?? [value.timeSlot];
126
126
  sameRecord(!(requested.timeZone && value.timeZone !== requested.timeZone || slots.some(slot => requested.serviceId && slot.serviceId !== requested.serviceId ||
127
127
  requested.localStartDate && (slot.localStartDate !== requested.localStartDate || slot.localEndDate !== requested.localEndDate))),
128
- "Wix returned availability for a different service, time or time zone.");
129
- return !events || !requested.serviceIds || sameRecord(slots.every(slot => requested.serviceIds.includes(slot.serviceId)), "Wix returned a class slot for a different service.");
128
+ "Wix returned availability for a different service, time or time zone.", { messageCode: "connectors.wix.returnedAvailabilityDifferentServiceTime" });
129
+ return !events || !requested.serviceIds || sameRecord(slots.every(slot => requested.serviceIds.includes(slot.serviceId)), "Wix returned a class slot for a different service.", { messageCode: "connectors.wix.returnedClassSlotDifferentService" });
130
130
  }
131
131
  function availabilityOperation(action, events = false) {
132
132
  const schema = createSchema({
@@ -164,13 +164,13 @@ function availabilityOperation(action, events = false) {
164
164
  scopes: [],
165
165
  request(input) {
166
166
  const { limit, cursor: next, ...body } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
167
- if (next && Object.keys(body).length) throw new ConnectorError("connector_input_invalid", "Use only cursor and limit for the next availability page.", { statusCode: 422 });
168
- if (body.customerChoices && !Object.keys(body.customerChoices).length) throw new ConnectorError("connector_input_invalid", "Select a duration or add-ons before supplying customer choices.", { statusCode: 422 });
167
+ if (next && Object.keys(body).length) throw new ConnectorError("connector_input_invalid", "Use only cursor and limit for the next availability page.", { statusCode: 422, messageCode: "connectors.wix.useCursorLimitNextAvailability" });
168
+ if (body.customerChoices && !Object.keys(body.customerChoices).length) throw new ConnectorError("connector_input_invalid", "Select a duration or add-ons before supplying customer choices.", { statusCode: 422, messageCode: "connectors.wix.selectDurationAddOnsSupplying" });
169
169
  if (!next && (!body.serviceId || !body.timeZone || (action === "list" ? !body.fromLocalDate || !body.toLocalDate || body.fromLocalDate >= body.toLocalDate : body.localStartDate >= body.localEndDate))) {
170
- throw new ConnectorError("connector_input_invalid", "Choose the service, time zone and an increasing local date range.", { statusCode: 422 });
170
+ throw new ConnectorError("connector_input_invalid", "Choose the service, time zone and an increasing local date range.", { statusCode: 422, messageCode: "connectors.wix.chooseServiceTimeZoneIncreasing" });
171
171
  }
172
172
  const locations = body.locations ?? (body.location ? [body.location] : []);
173
- if (locations.some(location => location.locationType === "BUSINESS" && !location.id)) throw new ConnectorError("connector_input_invalid", "Choose the business location ID.", { statusCode: 422 });
173
+ if (locations.some(location => location.locationType === "BUSINESS" && !location.id)) throw new ConnectorError("connector_input_invalid", "Choose the business location ID.", { statusCode: 422, messageCode: "connectors.wix.chooseBusinessLocationId" });
174
174
  const { serviceId, ...eventBody } = body;
175
175
  return { method: "POST", url: `https://www.wixapis.com/_api/service-availability/v2/time-slots${events ? "/event" : action === "get" ? "/get" : ""}`,
176
176
  body: action === "get" ? body : next ? { cursorPaging: { limit, cursor: next } } : { ...(events ? { ...eventBody, serviceIds: [serviceId], includeNonBookable: false } : { ...body, bookable: true }), cursorPaging: { limit } } };
@@ -208,7 +208,7 @@ function productReadOperation(version, action) {
208
208
  return { method: "POST", url: `${base}/query`,
209
209
  body: version === 1 ? { query: { paging }, includeVariants } : { query: { cursorPaging: paging } } };
210
210
  },
211
- validateResult: (value, request) => action === "get" ? validProduct(value?.product) && sameRecord(value.product.id === requestPath(request, -1), "Wix returned a different product.") : Array.isArray(value?.products) && value.products.length <= 100 && value.products.every(validProduct) &&
211
+ validateResult: (value, request) => action === "get" ? validProduct(value?.product) && sameRecord(value.product.id === requestPath(request, -1), "Wix returned a different product.", { messageCode: "connectors.wix.returnedDifferentProduct" }) : Array.isArray(value?.products) && value.products.length <= 100 && value.products.every(validProduct) &&
212
212
  (version === 1 ? (value.totalResults === undefined || Number.isSafeInteger(value.totalResults) && value.totalResults >= 0) &&
213
213
  (value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
214
214
  ["items", "offset"].every(key => value.metadata[key] === undefined || Number.isSafeInteger(value.metadata[key]) && value.metadata[key] >= 0)) :
@@ -321,17 +321,17 @@ function locationWriteOperation(action) {
321
321
  scopes: [],
322
322
  request(input) {
323
323
  const { locationId, ...location } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
324
- if (!Object.keys(location.address).length) throw new ConnectorError("connector_input_invalid", "Provide the location address.", { statusCode: 422 });
324
+ if (!Object.keys(location.address).length) throw new ConnectorError("connector_input_invalid", "Provide the location address.", { statusCode: 422, messageCode: "connectors.wix.provideLocationAddress" });
325
325
  if (location.businessSchedule && (["periods", "specialHourPeriod"].some(key => location.businessSchedule[key] !== undefined &&
326
326
  (!Array.isArray(location.businessSchedule[key]) || location.businessSchedule[key].length > 100)) ||
327
327
  Object.keys(location.businessSchedule).some(key => !["periods", "specialHourPeriod"].includes(key)))) {
328
- throw new ConnectorError("connector_input_invalid", "Use native businessSchedule periods and specialHourPeriod arrays with at most 100 entries each.", { statusCode: 422 });
328
+ throw new ConnectorError("connector_input_invalid", "Use native businessSchedule periods and specialHourPeriod arrays with at most 100 entries each.", { statusCode: 422, messageCode: "connectors.wix.businessScheduleLimits" });
329
329
  }
330
330
  return { method: action === "create" ? "POST" : "PUT", url: `https://www.wixapis.com/locations/v1/locations${locationId ? `/${locationId}` : ""}`,
331
331
  body: { location: { ...(locationId ? { id: locationId } : {}), ...location } } };
332
332
  },
333
333
  validateResult: (value, request) => validLocation(value?.location) && typeof value.location.revision === "string" && /^\d{1,19}$/u.test(value.location.revision) &&
334
- (action === "create" || sameRecord(value.location.id === requestPath(request, -1), "Wix returned a different business location."))
334
+ (action === "create" || sameRecord(value.location.id === requestPath(request, -1), "Wix returned a different business location.", { messageCode: "connectors.wix.returnedDifferentBusinessLocation" }))
335
335
  };
336
336
  }
337
337
  function locationActionOperation(action) {
@@ -343,7 +343,7 @@ function locationActionOperation(action) {
343
343
  return { method: "POST", url: `https://www.wixapis.com/locations/v1/locations/${locationId}/${action}`,
344
344
  body: {} };
345
345
  },
346
- validateResult: (value, request) => validLocation(value?.location) && sameRecord(value.location.id === requestPath(request, -2), "Wix returned a different business location.") &&
346
+ validateResult: (value, request) => validLocation(value?.location) && sameRecord(value.location.id === requestPath(request, -2), "Wix returned a different business location.", { messageCode: "connectors.wix.returnedDifferentBusinessLocation" }) &&
347
347
  (action === "archive" ? value.location.archived === true : value.location.default === true)
348
348
  };
349
349
  }
@@ -467,13 +467,13 @@ function bookingConfirmationOperation(action) {
467
467
  scopes: [],
468
468
  request(input) {
469
469
  const { bookingId, paymentStatus, revision, notifyParticipants, message } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
470
- if (message !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422 });
470
+ if (message !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422, messageCode: "connectors.wix.enableParticipantNotificationSupplyingMessage" });
471
471
  return { method: "POST", url: automatic ? `https://www.wixapis.com/bookings/v2/confirmation/${bookingId}:confirmOrDecline` :
472
472
  `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/${action}`,
473
473
  body: automatic ? { paymentStatus } : { revision, participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
474
474
  },
475
475
  validateResult: (value, request) => guid(value?.booking?.id) &&
476
- sameRecord(value.booking.id === (automatic ? requestPath(request, -1).split(":")[0] : requestPath(request, -2)), "Wix returned a different booking. Check the operation outcome before retrying.") &&
476
+ sameRecord(value.booking.id === (automatic ? requestPath(request, -1).split(":")[0] : requestPath(request, -2)), "Wix returned a different booking. Check the operation outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentBookingCheckOperation" }) &&
477
477
  typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
478
478
  BigInt(value.booking.revision) <= 9223372036854775807n && (automatic ? ["CONFIRMED", "PENDING", "DECLINED"].includes(value.booking.status) : value.booking.status === (action === "confirm" ? "CONFIRMED" : "DECLINED"))
479
479
  };
@@ -501,12 +501,12 @@ function fulfillmentWriteOperation(update) {
501
501
  request(input) {
502
502
  const { orderId, fulfillmentId, ...fulfillment } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
503
503
  if (!Object.keys(fulfillment).length || fulfillment.trackingInfo && !Object.keys(fulfillment.trackingInfo).length) {
504
- throw new ConnectorError("connector_input_invalid", "Supply the fulfillment properties to write.", { statusCode: 422 });
504
+ throw new ConnectorError("connector_input_invalid", "Supply the fulfillment properties to write.", { statusCode: 422, messageCode: "connectors.wix.supplyFulfillmentPropertiesWrite" });
505
505
  }
506
506
  const tracking = fulfillment.trackingInfo;
507
507
  if (!update && tracking && (!tracking.trackingNumber || !tracking.shippingProvider ||
508
508
  !["fedex", "ups", "usps", "dhl", "canadaPost"].includes(tracking.shippingProvider) && !tracking.trackingLink)) {
509
- throw new ConnectorError("connector_input_invalid", "Supply tracking number and provider; custom carriers also require a tracking URL.", { statusCode: 422 });
509
+ throw new ConnectorError("connector_input_invalid", "Supply tracking number and provider; custom carriers also require a tracking URL.", { statusCode: 422, messageCode: "connectors.wix.supplyTrackingNumberProviderCustom" });
510
510
  }
511
511
  return { method: update ? "PATCH" : "POST", url: update
512
512
  ? `https://www.wixapis.com/ecom/v1/fulfillments/${fulfillmentId}/orders/${encodeURIComponent(orderId)}`
@@ -514,12 +514,12 @@ function fulfillmentWriteOperation(update) {
514
514
  body: { fulfillment } };
515
515
  },
516
516
  validateResult: (value, request) => typeof value?.orderWithFulfillments?.orderId === "string" &&
517
- sameRecord(value.orderWithFulfillments.orderId === requestPath(request, update ? -1 : -2), "Wix returned fulfillments for a different order.") &&
517
+ sameRecord(value.orderWithFulfillments.orderId === requestPath(request, update ? -1 : -2), "Wix returned fulfillments for a different order.", { messageCode: "connectors.wix.returnedFulfillmentsDifferentOrder" }) &&
518
518
  Array.isArray(value.orderWithFulfillments.fulfillments) &&
519
519
  value.orderWithFulfillments.fulfillments.every(item => guid(item?.id) && Array.isArray(item.lineItems) && item.lineItems.length <= 300 &&
520
520
  item.lineItems.every(line => guid(line?.id) && Number.isInteger(line.quantity) && line.quantity >= 1 && line.quantity <= 100000)) &&
521
521
  (update ? sameRecord(value.orderWithFulfillments.fulfillments.some(item => item.id === requestPath(request, -3)),
522
- "Wix did not return the updated fulfillment. Inspect the order before retrying.") :
522
+ "Wix did not return the updated fulfillment. Inspect the order before retrying.", { messageCode: "connectors.wix.didNotReturnUpdatedFulfillment" }) :
523
523
  guid(value.fulfillmentId) && value.orderWithFulfillments.fulfillments.some(item => item.id === value.fulfillmentId))
524
524
  };
525
525
  }
@@ -559,22 +559,22 @@ const wixRuntime = {
559
559
  scopes: [],
560
560
  request(input) {
561
561
  const { bookingId, revision, slot, notifyParticipants, message } = validateSchemaPayload({ schema: bookingRescheduleSchema, mode: "replace" }, input, { statusCode: 422 });
562
- if (message !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422 });
562
+ if (message !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422, messageCode: "connectors.wix.enableParticipantNotificationSupplyingMessage" });
563
563
  if (slot.eventId !== undefined) {
564
564
  if (Object.keys(slot).length !== 1 || typeof slot.eventId !== "string" || slot.eventId.length < 36 || slot.eventId.length > 250 || /[\p{Cc}]/u.test(slot.eventId)) {
565
- throw new ConnectorError("connector_input_invalid", "For a class, supply only the selected eventId in slot.", { statusCode: 422 });
565
+ throw new ConnectorError("connector_input_invalid", "For a class, supply only the selected eventId in slot.", { statusCode: 422, messageCode: "connectors.wix.classSupplySelectedEventidSlot" });
566
566
  }
567
567
  } else {
568
568
  let validTimeZone = false;
569
569
  try { validTimeZone = typeof slot.timezone === "string" && !!new Intl.DateTimeFormat("en", { timeZone: slot.timezone }); } catch {}
570
570
  if (!guid(slot.serviceId) || !guid(slot.scheduleId) || !localDate(slot.startDate) || !localDate(slot.endDate) || slot.startDate >= slot.endDate || !validTimeZone) {
571
- throw new ConnectorError("connector_input_invalid", "For an appointment, supply its service/schedule IDs, increasing local slot dates and IANA timezone. Courses cannot be rescheduled.", { statusCode: 422 });
571
+ throw new ConnectorError("connector_input_invalid", "For an appointment, supply its service/schedule IDs, increasing local slot dates and IANA timezone. Courses cannot be rescheduled.", { statusCode: 422, messageCode: "connectors.wix.appointmentSupplyServiceScheduleIds" });
572
572
  }
573
573
  }
574
574
  return { method: "POST", url: `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/reschedule`,
575
575
  body: { revision, slot, participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
576
576
  },
577
- validateResult: (value, request) => guid(value?.booking?.id) && sameRecord(value.booking.id === requestPath(request, -2), "Wix returned a different booking. Check the operation outcome before retrying.") && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
577
+ validateResult: (value, request) => guid(value?.booking?.id) && sameRecord(value.booking.id === requestPath(request, -2), "Wix returned a different booking. Check the operation outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentBookingCheckOperation" }) && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
578
578
  BigInt(value.booking.revision) <= 9223372036854775807n && typeof value.booking.status === "string" && value.booking.status.length > 0
579
579
  },
580
580
  "bookings.confirm": bookingConfirmationOperation("confirm"),
@@ -600,7 +600,7 @@ const wixRuntime = {
600
600
  },
601
601
  validateResult(value, request) {
602
602
  const variants = value?.serviceVariants;
603
- return guid(variants?.id) && guid(variants.serviceId) && sameRecord(variants.serviceId === requestPath(request, -1), "Wix returned variants for a different service.") &&
603
+ return guid(variants?.id) && guid(variants.serviceId) && sameRecord(variants.serviceId === requestPath(request, -1), "Wix returned variants for a different service.", { messageCode: "connectors.wix.returnedVariantsDifferentService" }) &&
604
604
  Array.isArray(variants.options?.values) && variants.options.values.every(option => guid(option?.id) && typeof option.type === "string") &&
605
605
  Array.isArray(variants.variants?.values) && variants.variants.values.every(variant =>
606
606
  Array.isArray(variant?.choices) && variant.choices.every(choice => guid(choice?.optionId)));
@@ -612,13 +612,13 @@ const wixRuntime = {
612
612
  scopes: [],
613
613
  request(input) {
614
614
  const { serviceId, locations, removedLocationAction: action, moveToLocation, notifyParticipants, notificationMessage } = validateSchemaPayload({ schema: setServiceLocationsSchema, mode: "replace" }, input, { statusCode: 422 });
615
- if ((action === "MOVE_TO_LOCATION") !== (moveToLocation !== undefined)) throw new ConnectorError("connector_input_invalid", "Provide a destination only when moving future sessions.", { statusCode: 422 });
616
- if (notificationMessage !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422 });
615
+ if ((action === "MOVE_TO_LOCATION") !== (moveToLocation !== undefined)) throw new ConnectorError("connector_input_invalid", "Provide a destination only when moving future sessions.", { statusCode: 422, messageCode: "connectors.wix.provideDestinationMovingFutureSessions" });
616
+ if (notificationMessage !== undefined && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification before supplying a message.", { statusCode: 422, messageCode: "connectors.wix.enableParticipantNotificationSupplyingMessage" });
617
617
  for (const location of [...locations, ...(moveToLocation ? [moveToLocation] : [])]) {
618
618
  if (location.type === "BUSINESS" && (!location.business || location.custom) ||
619
619
  location.type === "CUSTOM" && (!location.custom || location.business || !Object.keys(location.custom.address).length) ||
620
620
  location.type === "CUSTOMER" && (location.business || location.custom)) {
621
- throw new ConnectorError("connector_input_invalid", "Supply the details matching each service location type.", { statusCode: 422 });
621
+ throw new ConnectorError("connector_input_invalid", "Supply the details matching each service location type.", { statusCode: 422, messageCode: "connectors.wix.supplyDetailsMatchingServiceLocation" });
622
622
  }
623
623
  }
624
624
  return { method: "POST", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}/locations`, body: {
@@ -626,35 +626,35 @@ const wixRuntime = {
626
626
  participantNotification: { notifyParticipants, ...(notificationMessage !== undefined ? { message: notificationMessage } : {}) }
627
627
  } };
628
628
  },
629
- validateResult: (value, request) => validService(value?.service) && sameRecord(value.service.id === requestPath(request, -2), "Wix returned a different booking service. Check the outcome before retrying.")
629
+ validateResult: (value, request) => validService(value?.service) && sameRecord(value.service.id === requestPath(request, -2), "Wix returned a different booking service. Check the outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentBookingServiceCheck" })
630
630
  },
631
631
  "bookingServices.create": {
632
632
  scopes: [],
633
633
  request(input) {
634
634
  const { service } = validateSchemaPayload({ schema: serviceCreateSchema, mode: "replace" }, input, { statusCode: 422 });
635
- const invalid = message => { throw new ConnectorError("connector_input_invalid", message, { statusCode: 422 }); };
636
- if (!["APPOINTMENT", "CLASS", "COURSE"].includes(service.type) || typeof service.name !== "string" || !service.name.trim() || service.name.length > 400) invalid("Provide a service type and name of up to 400 characters.");
637
- if (["id", "revision", "createdByAppId", "createdDate", "updatedDate", "addOnGroups"].some(key => Object.hasOwn(service, key))) invalid("Do not send generated service identity fields or existing add-on groups when creating a service.");
638
- if (!Number.isInteger(service.defaultCapacity) || service.defaultCapacity < 1 || service.defaultCapacity > 1000 || service.type === "APPOINTMENT" && service.defaultCapacity !== 1) invalid("Set appointment capacity to 1, or class/course capacity from 1 to 1000.");
635
+ const invalid = (message, codes) => { throw new ConnectorError("connector_input_invalid", message, { statusCode: 422, ...codes }); };
636
+ if (!["APPOINTMENT", "CLASS", "COURSE"].includes(service.type) || typeof service.name !== "string" || !service.name.trim() || service.name.length > 400) invalid("Provide a service type and name of up to 400 characters.", { messageCode: "connectors.wix.provideServiceTypeNameUp" });
637
+ if (["id", "revision", "createdByAppId", "createdDate", "updatedDate", "addOnGroups"].some(key => Object.hasOwn(service, key))) invalid("Do not send generated service identity fields or existing add-on groups when creating a service.", { messageCode: "connectors.wix.doNotSendGeneratedService" });
638
+ if (!Number.isInteger(service.defaultCapacity) || service.defaultCapacity < 1 || service.defaultCapacity > 1000 || service.type === "APPOINTMENT" && service.defaultCapacity !== 1) invalid("Set appointment capacity to 1, or class/course capacity from 1 to 1000.", { messageCode: "connectors.wix.setAppointmentCapacity1Class" });
639
639
  const online = service.onlineBooking;
640
- if (!online || typeof online.enabled !== "boolean" || ["requireManualApproval", "allowMultipleRequests"].some(key => online[key] !== undefined && typeof online[key] !== "boolean")) invalid("Provide explicit online-booking settings.");
640
+ if (!online || typeof online.enabled !== "boolean" || ["requireManualApproval", "allowMultipleRequests"].some(key => online[key] !== undefined && typeof online[key] !== "boolean")) invalid("Provide explicit online-booking settings.", { messageCode: "connectors.wix.provideExplicitOnlineBookingSettings" });
641
641
  const payment = service.payment;
642
- if (!payment || !["FIXED", "VARIED", "CUSTOM", "NO_FEE"].includes(payment.rateType)) invalid("Use FIXED, VARIED, CUSTOM or NO_FEE service payment settings.");
642
+ if (!payment || !["FIXED", "VARIED", "CUSTOM", "NO_FEE"].includes(payment.rateType)) invalid("Use FIXED, VARIED, CUSTOM or NO_FEE service payment settings.", { messageCode: "connectors.wix.useFixedVariedCustomNo" });
643
643
  const options = payment.options;
644
- if (!options || typeof options !== "object" || ["online", "inPerson", "deposit", "pricingPlan"].some(key => options[key] !== undefined && typeof options[key] !== "boolean") || !(options.online === true || options.inPerson === true)) invalid("Select in-person or online payment, including for a free service.");
645
- if (online.requireManualApproval && options.pricingPlan || !online.enabled && options.online) invalid("Manual approval cannot use pricing plans; online payment requires online booking.");
644
+ if (!options || typeof options !== "object" || ["online", "inPerson", "deposit", "pricingPlan"].some(key => options[key] !== undefined && typeof options[key] !== "boolean") || !(options.online === true || options.inPerson === true)) invalid("Select in-person or online payment, including for a free service.", { messageCode: "connectors.wix.selectPersonOnlinePaymentIncluding" });
645
+ if (online.requireManualApproval && options.pricingPlan || !online.enabled && options.online) invalid("Manual approval cannot use pricing plans; online payment requires online booking.", { messageCode: "connectors.wix.manualApprovalCannotUsePricing" });
646
646
  const priced = ["FIXED", "VARIED"].includes(payment.rateType);
647
- if ((options.online || options.deposit) && !priced) invalid("Online and deposit payments require fixed or varied pricing.");
647
+ if ((options.online || options.deposit) && !priced) invalid("Online and deposit payments require fixed or varied pricing.", { messageCode: "connectors.wix.onlineDepositPaymentsRequireFixed" });
648
648
  const rate = payment.rateType === "FIXED" ? payment.fixed : payment.varied;
649
649
  const money = value => value && typeof value.value === "string" && value.value.length <= 30 && /^\d+(?:\.\d{1,2})?$/u.test(value.value) && /[1-9]/u.test(value.value) && /^[A-Z]{3}$/u.test(value.currency);
650
- if (priced && !money(payment.rateType === "FIXED" ? rate?.price : rate?.defaultPrice)) invalid("Provide a positive decimal price string and three-letter currency code.");
651
- if (payment.rateType === "CUSTOM" && (typeof payment.custom?.description !== "string" || !payment.custom.description.length || payment.custom.description.length > 50)) invalid("Describe the custom payment in up to 50 characters.");
652
- if (options.deposit && (!options.online || options.inPerson || !money(rate?.deposit))) invalid("A deposit requires its amount, online payment and no in-person payment.");
650
+ if (priced && !money(payment.rateType === "FIXED" ? rate?.price : rate?.defaultPrice)) invalid("Provide a positive decimal price string and three-letter currency code.", { messageCode: "connectors.wix.providePositiveDecimalPriceString" });
651
+ if (payment.rateType === "CUSTOM" && (typeof payment.custom?.description !== "string" || !payment.custom.description.length || payment.custom.description.length > 50)) invalid("Describe the custom payment in up to 50 characters.", { messageCode: "connectors.wix.describeCustomPaymentUp50" });
652
+ if (options.deposit && (!options.online || options.inPerson || !money(rate?.deposit))) invalid("A deposit requires its amount, online payment and no in-person payment.", { messageCode: "connectors.wix.depositRequiresAmountOnlinePayment" });
653
653
  const durations = service.schedule?.availabilityConstraints?.sessionDurations;
654
654
  if (service.type === "APPOINTMENT") {
655
- if (!Array.isArray(service.staffMemberIds) || !service.staffMemberIds.length || !service.staffMemberIds.every(guid)) invalid("Select at least one staff resource ID for the appointment service.");
656
- if (!Array.isArray(durations) || !durations.length || !durations.every(value => Number.isInteger(value) && value > 0)) invalid("Provide appointment session durations in minutes.");
657
- } else if (durations !== undefined) invalid("Class/course session durations belong to their sessions, not service availability constraints.");
655
+ if (!Array.isArray(service.staffMemberIds) || !service.staffMemberIds.length || !service.staffMemberIds.every(guid)) invalid("Select at least one staff resource ID for the appointment service.", { messageCode: "connectors.wix.selectLeastStaffResourceId" });
656
+ if (!Array.isArray(durations) || !durations.length || !durations.every(value => Number.isInteger(value) && value > 0)) invalid("Provide appointment session durations in minutes.", { messageCode: "connectors.wix.provideAppointmentSessionDurationsMinutes" });
657
+ } else if (durations !== undefined) invalid("Class/course session durations belong to their sessions, not service availability constraints.", { messageCode: "connectors.wix.classCourseSessionDurationsBelong" });
658
658
  return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services", body: { service } };
659
659
  },
660
660
  validateResult: value => validService(value?.service)
@@ -668,7 +668,7 @@ const wixRuntime = {
668
668
  return { method: "GET", url: url.href };
669
669
  },
670
670
  validateResult: (value, request) => value?.dataItem?.data && typeof value.dataItem.data === "object" && !Array.isArray(value.dataItem.data) &&
671
- sameRecord(value.dataItem.id === requestPath(request, -1) && value.dataItem.dataCollectionId === new URL(request.url).searchParams.get("dataCollectionId"), "Wix returned a different CMS item or collection. Check write outcomes before retrying.")
671
+ sameRecord(value.dataItem.id === requestPath(request, -1) && value.dataItem.dataCollectionId === new URL(request.url).searchParams.get("dataCollectionId"), "Wix returned a different CMS item or collection. Check write outcomes before retrying.", { messageCode: "connectors.wix.returnedDifferentCmsItemCollection" })
672
672
  },
673
673
  "cmsItems.patch": {
674
674
  scopes: [],
@@ -678,7 +678,7 @@ const wixRuntime = {
678
678
  body: { dataCollectionId, patch: { dataItemId: itemId, fieldModifications }, ...(conditionFilter !== undefined ? { condition: { filter: conditionFilter } } : {}) } };
679
679
  },
680
680
  validateResult: (value, request) => value?.dataItem?.data && typeof value.dataItem.data === "object" && !Array.isArray(value.dataItem.data) &&
681
- sameRecord(value.dataItem.id === requestPath(request, -1) && value.dataItem.dataCollectionId === request.body.dataCollectionId, "Wix returned a different CMS item or collection. Check write outcomes before retrying.")
681
+ sameRecord(value.dataItem.id === requestPath(request, -1) && value.dataItem.dataCollectionId === request.body.dataCollectionId, "Wix returned a different CMS item or collection. Check write outcomes before retrying.", { messageCode: "connectors.wix.returnedDifferentCmsItemCollection" })
682
682
  },
683
683
  "cmsItems.list": {
684
684
  scopes: [],
@@ -692,7 +692,7 @@ const wixRuntime = {
692
692
  validateResult: (value, request) => Array.isArray(value?.dataItems) && value.dataItems.length <= 100 && value.dataItems.every(item =>
693
693
  typeof item?.id === "string" && item.id.length > 0 && collectionId(item.dataCollectionId) && item.data && typeof item.data === "object" && !Array.isArray(item.data)) &&
694
694
  (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata)) &&
695
- sameRecord(value.dataItems.every(item => item.dataCollectionId === request.body.dataCollectionId), "Wix returned items from a different CMS collection.")
695
+ sameRecord(value.dataItems.every(item => item.dataCollectionId === request.body.dataCollectionId), "Wix returned items from a different CMS collection.", { messageCode: "connectors.wix.returnedItemsDifferentCmsCollection" })
696
696
  },
697
697
  "cmsCollections.list": {
698
698
  scopes: [],
@@ -714,7 +714,7 @@ const wixRuntime = {
714
714
  return { method: "GET", url: `https://www.wixapis.com/wix-data/v2/collections/${id}?consistentRead=${value.consistentRead}` };
715
715
  },
716
716
  validateResult: (value, request) => validCollection(value?.collection) &&
717
- sameRecord(value.collection.id === new URL(request.url).pathname.split("/").slice(4).map(decodeURIComponent).join("/"), "Wix returned a different CMS collection.")
717
+ sameRecord(value.collection.id === new URL(request.url).pathname.split("/").slice(4).map(decodeURIComponent).join("/"), "Wix returned a different CMS collection.", { messageCode: "connectors.wix.returnedDifferentCmsCollection" })
718
718
  },
719
719
  "locations.archive": locationActionOperation("archive"),
720
720
  "locations.setDefault": locationActionOperation("set-default"),
@@ -737,7 +737,7 @@ const wixRuntime = {
737
737
  const { locationId } = validateSchemaPayload({ schema: locationGetSchema, mode: "replace" }, input, { statusCode: 422 });
738
738
  return { method: "GET", url: `https://www.wixapis.com/locations/v1/locations/${locationId}` };
739
739
  },
740
- validateResult: (value, request) => validLocation(value?.location) && sameRecord(value.location.id === requestPath(request, -1), "Wix returned a different business location.")
740
+ validateResult: (value, request) => validLocation(value?.location) && sameRecord(value.location.id === requestPath(request, -1), "Wix returned a different business location.", { messageCode: "connectors.wix.returnedDifferentBusinessLocation" })
741
741
  },
742
742
  "productsV1.list": productReadOperation(1, "list"),
743
743
  "productsV1.get": productReadOperation(1, "get"),
@@ -745,12 +745,12 @@ const wixRuntime = {
745
745
  scopes: [],
746
746
  request(input) {
747
747
  const { productId, price, ...changes } = validateSchemaPayload({ schema: productV1UpdateSchema, mode: "replace" }, input, { statusCode: 422 });
748
- if (!Object.keys(changes).length && price === undefined) throw new ConnectorError("connector_input_invalid", "Provide a product name, visibility or base-price change.", { statusCode: 422 });
748
+ if (!Object.keys(changes).length && price === undefined) throw new ConnectorError("connector_input_invalid", "Provide a product name, visibility or base-price change.", { statusCode: 422, messageCode: "connectors.wix.provideProductNameVisibilityBase" });
749
749
  return { method: "PATCH", url: `https://www.wixapis.com/stores/v1/products/${productId}`,
750
750
  body: { product: { ...changes, ...(price === undefined ? {} : { priceData: { price } }) } } };
751
751
  },
752
752
  validateResult: (value, request) => guid(value?.product?.id) && typeof value.product.name === "string" && value.product.name.length > 0 &&
753
- sameRecord(value.product.id === requestPath(request, -1), "Wix returned a different product.")
753
+ sameRecord(value.product.id === requestPath(request, -1), "Wix returned a different product.", { messageCode: "connectors.wix.returnedDifferentProduct" })
754
754
  },
755
755
  "productsV3.list": productReadOperation(3, "list"),
756
756
  "productsV3.get": productReadOperation(3, "get"),
@@ -758,12 +758,12 @@ const wixRuntime = {
758
758
  scopes: [],
759
759
  request(input) {
760
760
  const { productId, revision, ...changes } = validateSchemaPayload({ schema: productV3UpdateSchema, mode: "replace" }, input, { statusCode: 422 });
761
- if (!Object.keys(changes).length) throw new ConnectorError("connector_input_invalid", "Provide a product name or visibility change.", { statusCode: 422 });
761
+ if (!Object.keys(changes).length) throw new ConnectorError("connector_input_invalid", "Provide a product name or visibility change.", { statusCode: 422, messageCode: "connectors.wix.provideProductNameVisibilityChange" });
762
762
  return { method: "PATCH", url: `https://www.wixapis.com/stores/v3/products/${productId}`,
763
763
  body: { product: { id: productId, revision, ...changes } } };
764
764
  },
765
765
  validateResult: (value, request) => guid(value?.product?.id) && typeof value.product.name === "string" && value.product.name.length > 0 &&
766
- sameRecord(value.product.id === requestPath(request, -1), "Wix returned a different product.") &&
766
+ sameRecord(value.product.id === requestPath(request, -1), "Wix returned a different product.", { messageCode: "connectors.wix.returnedDifferentProduct" }) &&
767
767
  typeof value.product.revision === "string" && /^\d{1,19}$/u.test(value.product.revision) && BigInt(value.product.revision) <= 9223372036854775807n
768
768
  },
769
769
  "catalog.version": {
@@ -777,7 +777,7 @@ const wixRuntime = {
777
777
  scopes: [],
778
778
  request(input) {
779
779
  const { status, contactId, ...cursorPaging } = validateSchemaPayload({ schema: orderSearchSchema, mode: "replace" }, input, { statusCode: 422 });
780
- if (cursorPaging.cursor && (status || contactId)) throw new ConnectorError("connector_input_invalid", "Use only cursor and limit for the next order page.", { statusCode: 422 });
780
+ if (cursorPaging.cursor && (status || contactId)) throw new ConnectorError("connector_input_invalid", "Use only cursor and limit for the next order page.", { statusCode: 422, messageCode: "connectors.wix.useCursorLimitNextOrder" });
781
781
  const filter = { ...(status ? { status } : {}), ...(contactId ? { "buyerInfo.contactId": contactId } : {}) };
782
782
  return { method: "POST", url: "https://www.wixapis.com/ecom/v1/orders/search",
783
783
  body: { search: { cursorPaging, ...(Object.keys(filter).length ? { filter } : {}) } } };
@@ -794,7 +794,7 @@ const wixRuntime = {
794
794
  request(input) {
795
795
  const { orderId } = validateSchemaPayload({ schema: orderIdSchema, mode: "replace" }, input, { statusCode: 422 });
796
796
  return { method: "GET", url: `https://www.wixapis.com/ecom/v1/orders/${encodeURIComponent(orderId)}` };
797
- }, validateResult: (value, request) => validOrder(value?.order) && sameRecord(value.order.id === requestPath(request, -1), "Wix returned a different order.")
797
+ }, validateResult: (value, request) => validOrder(value?.order) && sameRecord(value.order.id === requestPath(request, -1), "Wix returned a different order.", { messageCode: "connectors.wix.returnedDifferentOrder" })
798
798
  },
799
799
  "orderFulfillments.create": fulfillmentWriteOperation(false),
800
800
  "orderFulfillments.update": fulfillmentWriteOperation(true),
@@ -805,7 +805,7 @@ const wixRuntime = {
805
805
  return { method: "GET", url: `https://www.wixapis.com/ecom/v1/fulfillments/orders/${encodeURIComponent(orderId)}` };
806
806
  },
807
807
  validateResult: (value, request) => guid(value?.orderWithFulfillments?.orderId) &&
808
- sameRecord(value.orderWithFulfillments.orderId === requestPath(request, -1), "Wix returned fulfillments for a different order.") &&
808
+ sameRecord(value.orderWithFulfillments.orderId === requestPath(request, -1), "Wix returned fulfillments for a different order.", { messageCode: "connectors.wix.returnedFulfillmentsDifferentOrder" }) &&
809
809
  Array.isArray(value.orderWithFulfillments.fulfillments) &&
810
810
  value.orderWithFulfillments.fulfillments.every(item => guid(item?.id) && Array.isArray(item.lineItems) && item.lineItems.length <= 300 &&
811
811
  item.lineItems.every(line => guid(line?.id) && Number.isInteger(line.quantity) && line.quantity >= 1 && line.quantity <= 100000))
@@ -819,7 +819,7 @@ const wixRuntime = {
819
819
  url.searchParams.set("timeZone", timeZone);
820
820
  return { method: "GET", url: url.href };
821
821
  }, validateResult: (value, request) => validSlot(value?.timeSlot) && typeof value.timeSlot.eventInfo?.eventId === "string" && typeof value.timeZone === "string" &&
822
- sameRecord(value.timeSlot.eventInfo.eventId === requestPath(request, -1) && value.timeZone === new URL(request.url).searchParams.get("timeZone"), "Wix returned a different event slot or time zone.")
822
+ sameRecord(value.timeSlot.eventInfo.eventId === requestPath(request, -1) && value.timeZone === new URL(request.url).searchParams.get("timeZone"), "Wix returned a different event slot or time zone.", { messageCode: "connectors.wix.returnedDifferentEventSlotTime" })
823
823
  },
824
824
  "forms.summary": {
825
825
  scopes: [],
@@ -827,7 +827,7 @@ const wixRuntime = {
827
827
  const { formId } = validateSchemaPayload({ schema: formSummarySchema, mode: "replace" }, input, { statusCode: 422 });
828
828
  return { method: "GET", url: `https://www.wixapis.com/form-schema-service/v4/forms/${formId}/summary` };
829
829
  },
830
- validateResult: (value, request) => guid(value?.formSummary?.id) && sameRecord(value.formSummary.id === requestPath(request, -2), "Wix returned a different form summary.") &&
830
+ validateResult: (value, request) => guid(value?.formSummary?.id) && sameRecord(value.formSummary.id === requestPath(request, -2), "Wix returned a different form summary.", { messageCode: "connectors.wix.returnedDifferentFormSummary" }) &&
831
831
  Array.isArray(value.formSummary.fields) && value.formSummary.fields.length <= 500 &&
832
832
  value.formSummary.fields.every(field => guid(field?.id) && typeof field.target === "string" && field.target.length > 0 && field.target.length <= 200 &&
833
833
  typeof field.type === "string" && field.type.length > 0 && (field.label === undefined || typeof field.label === "string" && field.label.length <= 350) &&
@@ -839,18 +839,18 @@ const wixRuntime = {
839
839
  const value = validateSchemaPayload({ schema: bookingCreateSchema, mode: "replace" }, input, { statusCode: 422 });
840
840
  const { kind, serviceId, timeZone, startDate, endDate, eventId, scheduleId, resourceId, location, totalParticipants, participantsChoices, bookedAddOns, formSubmission, notifyParticipants, sendSmsReminder } = value;
841
841
  if (participantsChoices) {
842
- if (totalParticipants !== undefined) throw new ConnectorError("connector_input_invalid", "Use participant choices or a fixed participant total, not both.", { statusCode: 422 });
842
+ if (totalParticipants !== undefined) throw new ConnectorError("connector_input_invalid", "Use participant choices or a fixed participant total, not both.", { statusCode: 422, messageCode: "connectors.wix.useParticipantChoicesFixedParticipant" });
843
843
  if (participantsChoices.serviceChoices.reduce((total, group) => total + group.numberOfParticipants, 0) > 1000 ||
844
844
  participantsChoices.serviceChoices.some(group => new Set(group.choices.map(choice => choice.optionId)).size !== group.choices.length ||
845
845
  group.choices.some(choice => (choice.custom !== undefined) === (choice.duration !== undefined)))) {
846
- throw new ConnectorError("connector_input_invalid", "Use at most 1000 participants, distinct options per group and exactly one custom or duration value per choice.", { statusCode: 422 });
846
+ throw new ConnectorError("connector_input_invalid", "Use at most 1000 participants, distinct options per group and exactly one custom or duration value per choice.", { statusCode: 422, messageCode: "connectors.wix.useMost1000ParticipantsDistinct" });
847
847
  }
848
848
  }
849
849
  if (kind === "APPOINTMENT" ? !startDate || !endDate || startDate >= endDate || !resourceId || !location || eventId || scheduleId :
850
850
  kind === "CLASS" ? !eventId || startDate || endDate || resourceId || location || scheduleId : !scheduleId || startDate || endDate || resourceId || location || eventId) {
851
- throw new ConnectorError("connector_input_invalid", "Use appointment dates/resource/location, a class event ID, or a course schedule ID, matching the selected service type.", { statusCode: 422 });
851
+ throw new ConnectorError("connector_input_invalid", "Use appointment dates/resource/location, a class event ID, or a course schedule ID, matching the selected service type.", { statusCode: 422, messageCode: "connectors.wix.useAppointmentDatesResourceLocation" });
852
852
  }
853
- if (location?.locationType === "OWNER_BUSINESS" && !location.id) throw new ConnectorError("connector_input_invalid", "Choose the business location ID.", { statusCode: 422 });
853
+ if (location?.locationType === "OWNER_BUSINESS" && !location.id) throw new ConnectorError("connector_input_invalid", "Choose the business location ID.", { statusCode: 422, messageCode: "connectors.wix.chooseBusinessLocationId" });
854
854
  const bookedEntity = kind === "COURSE" ? { schedule: { scheduleId, serviceId, timezone: timeZone } } :
855
855
  { slot: { serviceId, timezone: timeZone, ...(kind === "CLASS" ? { eventId } : { startDate, endDate, resource: { id: resourceId }, location }) } };
856
856
  return { method: "POST", url: "https://www.wixapis.com/_api/bookings-service/v2/bookings",
@@ -878,18 +878,18 @@ const wixRuntime = {
878
878
  ["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
879
879
  (value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.extendedBookings.length)) &&
880
880
  (!request.body.query.filter?.id || sameRecord(value.extendedBookings.length <= 1 && value.extendedBookings.every(record => record.booking.id === request.body.query.filter.id),
881
- "Wix returned an unexpected booking for the requested ID."))
881
+ "Wix returned an unexpected booking for the requested ID.", { messageCode: "connectors.wix.returnedUnexpectedBookingRequestedId" }))
882
882
  },
883
883
  "bookings.cancel": {
884
884
  scopes: [],
885
885
  request(input) {
886
886
  const { bookingId, revision, notifyParticipants, message, waiveCharges } = validateSchemaPayload({ schema: bookingCancelSchema, mode: "replace" }, input, { statusCode: 422 });
887
- if (message && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification to send a cancellation message.", { statusCode: 422 });
887
+ if (message && !notifyParticipants) throw new ConnectorError("connector_input_invalid", "Enable participant notification to send a cancellation message.", { statusCode: 422, messageCode: "connectors.wix.enableParticipantNotificationSendCancellation" });
888
888
  return { method: "POST", url: `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/cancel`,
889
889
  body: { revision, waiveCharges,
890
890
  participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
891
891
  },
892
- validateResult: (value, request) => guid(value?.booking?.id) && sameRecord(value.booking.id === requestPath(request, -2), "Wix returned a different booking. Check the operation outcome before retrying.") && value.booking.status === "CANCELED" &&
892
+ validateResult: (value, request) => guid(value?.booking?.id) && sameRecord(value.booking.id === requestPath(request, -2), "Wix returned a different booking. Check the operation outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentBookingCheckOperation" }) && value.booking.status === "CANCELED" &&
893
893
  typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) && BigInt(value.booking.revision) <= 9223372036854775807n
894
894
  },
895
895
  "bookingServices.list": {
@@ -908,19 +908,19 @@ const wixRuntime = {
908
908
  request(input) {
909
909
  const { serviceId } = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
910
910
  return { method: "GET", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}` };
911
- }, validateResult: (value, request) => validService(value?.service) && sameRecord(value.service.id === requestPath(request, -1), "Wix returned a different booking service. Check the outcome before retrying.")
911
+ }, validateResult: (value, request) => validService(value?.service) && sameRecord(value.service.id === requestPath(request, -1), "Wix returned a different booking service. Check the outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentBookingServiceCheck" })
912
912
  },
913
913
  "bookingServices.update": {
914
914
  scopes: [],
915
915
  request(input) {
916
916
  const { serviceId, ...service } = validateSchemaPayload({ schema: serviceUpdateSchema, mode: "replace" }, input, { statusCode: 422 });
917
- if (Object.keys(service).length < 2) throw new ConnectorError("connector_input_invalid", "Choose a service field to update.", { statusCode: 422 });
917
+ if (Object.keys(service).length < 2) throw new ConnectorError("connector_input_invalid", "Choose a service field to update.", { statusCode: 422, messageCode: "connectors.wix.chooseServiceFieldUpdate" });
918
918
  if (["payment", "onlineBooking"].some(key => service[key] !== undefined && !Object.keys(service[key]).length) ||
919
919
  service.schedule && !Object.keys(service.schedule.availabilityConstraints).length) {
920
- throw new ConnectorError("connector_input_invalid", "Provide the selected service settings instead of an empty object.", { statusCode: 422 });
920
+ throw new ConnectorError("connector_input_invalid", "Provide the selected service settings instead of an empty object.", { statusCode: 422, messageCode: "connectors.wix.provideSelectedServiceSettingsInstead" });
921
921
  }
922
922
  return { method: "PATCH", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}`, body: { service: { id: serviceId, ...service } } };
923
- }, validateResult: (value, request) => validService(value?.service) && sameRecord(value.service.id === requestPath(request, -1), "Wix returned a different booking service. Check the outcome before retrying.")
923
+ }, validateResult: (value, request) => validService(value?.service) && sameRecord(value.service.id === requestPath(request, -1), "Wix returned a different booking service. Check the outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentBookingServiceCheck" })
924
924
  },
925
925
  "contacts.get": contactOperation("get"),
926
926
  "contacts.create": contactOperation("create"),
@@ -962,10 +962,10 @@ function wixSiteOperation(operation) {
962
962
  let siteId = settings.siteId, values = input;
963
963
  if (input && Object.hasOwn(input, "siteId")) {
964
964
  ({ siteId, ...values } = input);
965
- if (!guid(siteId)) throw new ConnectorError("connector_input_invalid", "Choose a valid Wix Site ID.", { statusCode: 422 });
965
+ if (!guid(siteId)) throw new ConnectorError("connector_input_invalid", "Choose a valid Wix Site ID.", { statusCode: 422, messageCode: "connectors.wix.chooseValidSiteId" });
966
966
  const allowed = (settings.selectableSiteIds || "").split(",").map(id => id.trim().toLowerCase());
967
- if (!allowed.includes(siteId.toLowerCase())) throw new ConnectorError("connector_input_invalid", "This site is not enabled in the project's Selectable Site IDs.", { statusCode: 422 });
968
- } else if (!guid(siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422 });
967
+ if (!allowed.includes(siteId.toLowerCase())) throw new ConnectorError("connector_input_invalid", "This site is not enabled in the project's Selectable Site IDs.", { statusCode: 422, messageCode: "connectors.wix.siteNotEnabledProjectSelectable" });
968
+ } else if (!guid(siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422, messageCode: "connectors.wix.configureSiteIdFirst" });
969
969
  const request = operation.request(values, { ...settings, siteId });
970
970
  return { ...request, headers: { ...request.headers, "wix-site-id": siteId } };
971
971
  }
package/src/server/wiz.js CHANGED
@@ -8,15 +8,15 @@ import { validateIntegrationConfiguration } from "@jskit-ai/connectors-core/shar
8
8
  import { wizDefinition } from "../shared/wiz.js";
9
9
 
10
10
  const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
11
- const failure = (code, message, statusCode = 502) => new ConnectorError(code, message, { statusCode });
11
+ const failure = (code, message, statusCode = 502, codes = {}) => new ConnectorError(code, message, { statusCode, ...codes });
12
12
 
13
13
  // This process belongs to the scanner, not the application's chat/agent runtime.
14
14
  async function runCli(executable, args, options, signal, timeoutMs) {
15
- if (signal?.aborted) throw failure("connector_cancelled", "The Wiz scan was cancelled.", 499);
15
+ if (signal?.aborted) throw failure("connector_cancelled", "The Wiz scan was cancelled.", 499, { messageCode: "connectors.wiz.scanCancelled" });
16
16
  return new Promise((resolve, reject) => {
17
17
  let child;
18
18
  try { child = spawn(executable, args, { ...options, shell: false, detached: true, stdio: "ignore" }); }
19
- catch { reject(failure("connector_runner_unavailable", "The Wiz CLI could not be started.")); return; }
19
+ catch { reject(failure("connector_runner_unavailable", "The Wiz CLI could not be started.", 502, { messageCode: "connectors.wiz.cliCouldNotStarted" })); return; }
20
20
  let cancelled = false; let timedOut = false; let startFailed = false;
21
21
  const kill = () => {
22
22
  if (!child.pid) return;
@@ -32,9 +32,9 @@ async function runCli(executable, args, options, signal, timeoutMs) {
32
32
  // Wait for close before deleting reports/cache, including after cancellation.
33
33
  child.once("close", (exitCode) => {
34
34
  clearTimeout(timer); signal?.removeEventListener("abort", abort); kill();
35
- if (cancelled) reject(failure("connector_cancelled", "The Wiz scan was cancelled.", 499));
36
- else if (timedOut) reject(failure("connector_timeout", "The Wiz scan exceeded its time limit.", 504));
37
- else if (startFailed) reject(failure("connector_runner_unavailable", "The Wiz CLI could not be started."));
35
+ if (cancelled) reject(failure("connector_cancelled", "The Wiz scan was cancelled.", 499, { messageCode: "connectors.wiz.scanCancelled" }));
36
+ else if (timedOut) reject(failure("connector_timeout", "The Wiz scan exceeded its time limit.", 504, { messageCode: "connectors.wiz.scanExceededTimeLimit" }));
37
+ else if (startFailed) reject(failure("connector_runner_unavailable", "The Wiz CLI could not be started.", 502, { messageCode: "connectors.wiz.cliCouldNotStarted" }));
38
38
  else resolve(exitCode);
39
39
  });
40
40
  });
@@ -58,29 +58,29 @@ function createWizScanner({ configuration, authorize, resolveReference, resolveS
58
58
 
59
59
  async function scan({ context, integrationId, signal }) {
60
60
  const integration = Object.hasOwn(config.integrations, integrationId) ? config.integrations[integrationId] : null;
61
- if (integration?.provider !== "wiz") throw failure("connector_not_found", "This Wiz integration is not configured.", 404);
61
+ if (integration?.provider !== "wiz") throw failure("connector_not_found", "This Wiz integration is not configured.", 404, { messageCode: "connectors.wiz.integrationNotConfigured" });
62
62
  const owner = await authorize(context, { integrationId, operation: "source.scan", accountMode: "shared" });
63
- if (!owner?.applicationId || !owner?.subjectId) throw failure("connector_access_denied", "Access to this source scan was denied.", 403);
63
+ if (!owner?.applicationId || !owner?.subjectId) throw failure("connector_access_denied", "Access to this source scan was denied.", 403, { messageCode: "connectors.wiz.accessSourceScanDenied" });
64
64
  const registration = config.registrations[integration.authentication.registrationRef];
65
65
  if (integration.settings.tokenUrl !== "https://auth.app.wiz.io/oauth/token") {
66
- throw failure("connector_authentication_unsupported", "Auth0 scanning requires a verified compatible Wiz runner.", 501);
66
+ throw failure("connector_authentication_unsupported", "Auth0 scanning requires a verified compatible Wiz runner.", 501, { messageCode: "connectors.wiz.auth0ScanningRequiresVerifiedCompatible" });
67
67
  }
68
- if (signal?.aborted) throw failure("connector_cancelled", "The Wiz scan was cancelled.", 499);
68
+ if (signal?.aborted) throw failure("connector_cancelled", "The Wiz scan was cancelled.", 499, { messageCode: "connectors.wiz.scanCancelled" });
69
69
  // The browser supplies no filesystem path. The trusted host selects an immutable
70
70
  // source snapshot and revision after checking the requesting principal's access.
71
71
  const target = await resolveScanTarget({ ...owner }, { integrationId, signal });
72
72
  if (!object(target) || typeof target.directory !== "string" || !path.isAbsolute(target.directory) ||
73
73
  target.directory.includes("\0") || typeof target.revision !== "string" || !target.revision.trim() || target.revision.length > 512) {
74
- throw failure("connector_source_invalid", "The host must supply an absolute source directory and revision.", 422);
74
+ throw failure("connector_source_invalid", "The host must supply an absolute source directory and revision.", 422, { messageCode: "connectors.wiz.hostMustSupplyAbsoluteSource" });
75
75
  }
76
76
  let directory;
77
77
  try { directory = await realpath(target.directory); if (!(await stat(directory)).isDirectory()) throw new Error(); }
78
- catch { throw failure("connector_source_unavailable", "The authorised source directory is unavailable.", 422); }
78
+ catch { throw failure("connector_source_unavailable", "The authorised source directory is unavailable.", 422, { messageCode: "connectors.wiz.authorisedSourceDirectoryUnavailable" }); }
79
79
  let secret;
80
80
  try { secret = await resolveReference(registration.clientSecretRef, { ...owner }); }
81
- catch { throw failure("connector_binding_missing", "The Wiz client-secret reference is unavailable.", 422); }
81
+ catch { throw failure("connector_binding_missing", "The Wiz client-secret reference is unavailable.", 422, { messageCode: "connectors.wiz.clientSecretReferenceUnavailable" }); }
82
82
  if (typeof secret !== "string" || !secret.trim() || secret.includes("\0") || secret.length > 16384) {
83
- throw failure("connector_binding_missing", "The Wiz client-secret reference is invalid.", 422);
83
+ throw failure("connector_binding_missing", "The Wiz client-secret reference is invalid.", 422, { messageCode: "connectors.wiz.clientSecretReferenceInvalid" });
84
84
  }
85
85
  const temporary = await mkdtemp(path.join(tmpdir(), "jskit-wiz-"));
86
86
  try {
@@ -91,8 +91,8 @@ function createWizScanner({ configuration, authorize, resolveReference, resolveS
91
91
  const exitCode = await runCli(executable, args, { cwd: temporary,
92
92
  env: { ...env, HOME: temporary, TMPDIR: temporary, WIZ_DIR: temporary, WIZ_CLIENT_ID: registration.clientId, WIZ_CLIENT_SECRET: secret }
93
93
  }, signal, timeoutMs);
94
- if (exitCode === 3) throw failure("connector_authentication_failed", "Wiz rejected the service-account credentials.", 401);
95
- if (exitCode !== 0 && exitCode !== 4) throw failure("connector_scan_failed", "Wiz could not complete the scan. Check the runner, policies and tenant access.");
94
+ if (exitCode === 3) throw failure("connector_authentication_failed", "Wiz rejected the service-account credentials.", 401, { messageCode: "connectors.wiz.rejectedServiceAccountCredentials" });
95
+ if (exitCode !== 0 && exitCode !== 4) throw failure("connector_scan_failed", "Wiz could not complete the scan. Check the runner, policies and tenant access.", 502, { messageCode: "connectors.wiz.couldNotCompleteScanCheck" });
96
96
  let report;
97
97
  try {
98
98
  const file = await open(reportPath, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
@@ -101,11 +101,11 @@ function createWizScanner({ configuration, authorize, resolveReference, resolveS
101
101
  if (!info.isFile() || info.size > maxReportBytes) throw new Error();
102
102
  report = JSON.parse(await file.readFile("utf8"));
103
103
  } finally { await file.close(); }
104
- } catch { throw failure("connector_response_invalid", "Wiz did not produce a readable report within the configured size limit."); }
104
+ } catch { throw failure("connector_response_invalid", "Wiz did not produce a readable report within the configured size limit.", 502, { messageCode: "connectors.wiz.didNotProduceReadableReport" }); }
105
105
  const verdict = report?.status?.verdict;
106
106
  if (!object(report) || report.status?.state !== "SUCCESS" || !object(report.result) || !["PASSED_BY_POLICY", "WARN_BY_POLICY", "FAILED_BY_POLICY"].includes(verdict) ||
107
107
  (exitCode === 4 && verdict !== "FAILED_BY_POLICY")) {
108
- throw failure("connector_response_invalid", "Wiz returned an unsupported or inconsistent scan report.");
108
+ throw failure("connector_response_invalid", "Wiz returned an unsupported or inconsistent scan report.", 502, { messageCode: "connectors.wiz.returnedUnsupportedInconsistentScanReport" });
109
109
  }
110
110
  return { provider: "wiz", integrationId, revision: target.revision,
111
111
  policyStatus: verdict === "FAILED_BY_POLICY" ? "blocked" : verdict === "WARN_BY_POLICY" ? "warning" : "passed", report };