@jskit-ai/connectors-catalog 0.2.3 → 0.2.4

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 (192) hide show
  1. package/README.md +4 -4
  2. package/docs/ai.md +3 -3
  3. package/docs/airtable.md +1 -1
  4. package/docs/algolia.md +2 -2
  5. package/docs/amazon-redshift.md +2 -2
  6. package/docs/apollo-io.md +2 -2
  7. package/docs/asana.md +2 -2
  8. package/docs/ashby.md +1 -1
  9. package/docs/attention.md +2 -2
  10. package/docs/aws-athena.md +1 -1
  11. package/docs/aws-credentials.md +3 -3
  12. package/docs/aws-s3.md +1 -1
  13. package/docs/bigquery.md +2 -2
  14. package/docs/brevo.md +2 -2
  15. package/docs/calendly.md +3 -3
  16. package/docs/canva.md +2 -2
  17. package/docs/chargebee.md +2 -2
  18. package/docs/clay.md +2 -2
  19. package/docs/clickhouse.md +1 -1
  20. package/docs/confidence-flags.md +1 -1
  21. package/docs/contentful.md +1 -1
  22. package/docs/dbt-semantic-layer.md +2 -2
  23. package/docs/elevenlabs.md +2 -2
  24. package/docs/firebase-cloud-messaging.md +3 -3
  25. package/docs/firecrawl.md +4 -4
  26. package/docs/fireflies.md +1 -1
  27. package/docs/gatewayapi.md +1 -1
  28. package/docs/gemini-enterprise.md +1 -1
  29. package/docs/github-api.md +1 -1
  30. package/docs/gitlab-api.md +1 -1
  31. package/docs/gmail.md +1 -1
  32. package/docs/gong.md +1 -1
  33. package/docs/google-ads.md +3 -3
  34. package/docs/google-analytics.md +2 -2
  35. package/docs/google-docs.md +1 -1
  36. package/docs/google-drive.md +1 -1
  37. package/docs/google-maps-platform.md +2 -2
  38. package/docs/google-search-console.md +1 -1
  39. package/docs/google-sheets.md +1 -1
  40. package/docs/google-slides.md +2 -2
  41. package/docs/granola.md +2 -2
  42. package/docs/hex.md +2 -2
  43. package/docs/heygen.md +2 -2
  44. package/docs/inngest.md +1 -1
  45. package/docs/klipy.md +1 -1
  46. package/docs/lightspeed.md +2 -2
  47. package/docs/linear.md +1 -1
  48. package/docs/linkedin.md +2 -2
  49. package/docs/mailgun.md +1 -1
  50. package/docs/microsoft-excel.md +2 -2
  51. package/docs/microsoft-fabric.md +1 -1
  52. package/docs/microsoft-outlook.md +1 -1
  53. package/docs/microsoft-powerpoint.md +1 -1
  54. package/docs/microsoft-teams.md +1 -1
  55. package/docs/microsoft-word.md +1 -1
  56. package/docs/notion.md +1 -1
  57. package/docs/oura.md +3 -3
  58. package/docs/paddle.md +3 -3
  59. package/docs/pipedrive.md +2 -2
  60. package/docs/prestashop.md +1 -1
  61. package/docs/replicate.md +1 -1
  62. package/docs/resend.md +1 -1
  63. package/docs/salesforce.md +1 -1
  64. package/docs/sanity.md +3 -3
  65. package/docs/semrush.md +2 -2
  66. package/docs/sentry.md +1 -1
  67. package/docs/slack.md +1 -1
  68. package/docs/snowflake.md +5 -5
  69. package/docs/storyblok.md +1 -1
  70. package/docs/stripe.md +2 -2
  71. package/docs/tally.md +1 -1
  72. package/docs/telegram.md +2 -2
  73. package/docs/tiktok.md +3 -3
  74. package/docs/twilio.md +3 -3
  75. package/docs/twitch.md +2 -2
  76. package/docs/wave.md +3 -3
  77. package/docs/wix.md +13 -13
  78. package/docs/wordpress-com.md +1 -1
  79. package/docs/wordpress-self-hosted.md +1 -1
  80. package/docs/workday.md +2 -2
  81. package/docs/x-twitter.md +3 -3
  82. package/docs/xero.md +3 -3
  83. package/docs/zoho-books.md +2 -2
  84. package/docs/zoho-crm.md +2 -2
  85. package/package.json +4 -3
  86. package/patterns/ai-connections/PATTERN.md +16 -1
  87. package/patterns/ai-connections/example/ai-model.js +12 -3
  88. package/patterns/ai-connections/example/perplexity-answer.js +15 -4
  89. package/patterns/api-key-connection/PATTERN.md +9 -1
  90. package/patterns/api-key-connection/example/connections.js +5 -2
  91. package/patterns/aws-storage-queries/PATTERN.md +13 -3
  92. package/patterns/aws-storage-queries/example/s3-transfer.js +14 -3
  93. package/patterns/event-delivery/PATTERN.md +1 -1
  94. package/patterns/firebase-messaging/PATTERN.md +10 -0
  95. package/patterns/firebase-messaging/example/server/notifications.js +8 -0
  96. package/patterns/google-ads-search/PATTERN.md +18 -7
  97. package/patterns/google-ads-search/example/ads-setup.js +13 -5
  98. package/patterns/oauth-connection/PATTERN.md +1 -1
  99. package/patterns/paddle-catalogue/PATTERN.md +11 -2
  100. package/patterns/paddle-catalogue/example/create-products.js +5 -2
  101. package/patterns/public-image/PATTERN.md +10 -1
  102. package/patterns/public-image/example/logo-url.js +4 -1
  103. package/patterns/redshift-queries/PATTERN.md +1 -1
  104. package/patterns/source-scanning/PATTERN.md +13 -2
  105. package/patterns/source-scanning/example/source-scanner.js +9 -0
  106. package/src/server/ai.js +19 -5
  107. package/src/server/apify.js +2 -2
  108. package/src/server/asana.js +6 -6
  109. package/src/server/ashby.js +2 -1
  110. package/src/server/attention.js +12 -11
  111. package/src/server/awsTransport.js +6 -0
  112. package/src/server/bigquery.js +5 -4
  113. package/src/server/brevo.js +2 -1
  114. package/src/server/clay.js +2 -1
  115. package/src/server/clickhouse.js +2 -1
  116. package/src/server/confidence.js +6 -1
  117. package/src/server/connectorValues.js +33 -0
  118. package/src/server/databricks.js +7 -7
  119. package/src/server/dbt-semantic-layer.js +5 -5
  120. package/src/server/elevenlabs.js +2 -2
  121. package/src/server/firebase-cloud-messaging.js +2 -2
  122. package/src/server/gemini-enterprise.js +5 -5
  123. package/src/server/google-ads-search.js +68 -48
  124. package/src/server/google-ads.js +15 -17
  125. package/src/server/google-drive.js +7 -7
  126. package/src/server/google-maps-platform.js +4 -2
  127. package/src/server/google-search-console.js +4 -3
  128. package/src/server/google.js +8 -4
  129. package/src/server/googleAdsApi.js +14 -0
  130. package/src/server/granola.js +2 -2
  131. package/src/server/hubspot.js +2 -1
  132. package/src/server/incident-io.js +3 -2
  133. package/src/server/lexware.js +5 -4
  134. package/src/server/lightspeed.js +7 -5
  135. package/src/server/linkedin.js +2 -2
  136. package/src/server/mcpTransport.js +8 -4
  137. package/src/server/microsoft-fabric.js +3 -3
  138. package/src/server/microsoft-onedrive.js +2 -2
  139. package/src/server/microsoft-onenote.js +4 -3
  140. package/src/server/microsoft-outlook.js +6 -6
  141. package/src/server/microsoft-sharepoint.js +4 -2
  142. package/src/server/microsoft-teams.js +2 -1
  143. package/src/server/microsoft.js +20 -13
  144. package/src/server/notion.js +2 -1
  145. package/src/server/pipedrive.js +2 -1
  146. package/src/server/salesforce.js +12 -12
  147. package/src/server/semrush.js +9 -8
  148. package/src/server/snowflake.js +20 -20
  149. package/src/server/storyblok.js +2 -1
  150. package/src/server/tally.js +2 -1
  151. package/src/server/telegram.js +2 -6
  152. package/src/server/tiktok.js +3 -3
  153. package/src/server/twilio.js +3 -2
  154. package/src/server/twitch.js +5 -3
  155. package/src/server/types.js +13 -0
  156. package/src/server/wix.js +20 -878
  157. package/src/server/wixBookings.js +449 -0
  158. package/src/server/wixCms.js +132 -0
  159. package/src/server/wixStores.js +296 -0
  160. package/src/server/wixValues.js +35 -0
  161. package/src/server/wiz.js +18 -4
  162. package/src/server/woocommerce.js +1 -1
  163. package/src/server/wordpress-com.js +2 -1
  164. package/src/server/workday.js +9 -9
  165. package/src/server/x-twitter.js +2 -1
  166. package/src/server/xero.js +40 -40
  167. package/src/server/zoho-books.js +6 -6
  168. package/src/server/zoho-crm.js +5 -5
  169. package/src/shared/confidence.js +6 -1
  170. package/src/shared/dbt-semantic-layer.js +1 -1
  171. package/src/shared/figma.js +1 -1
  172. package/src/shared/google.js +8 -4
  173. package/src/shared/googleScope.js +4 -0
  174. package/src/shared/microsoft-fabric.js +4 -5
  175. package/src/shared/microsoft.js +8 -3
  176. package/src/shared/salesforce.js +1 -1
  177. package/src/shared/tokens.js +7 -3
  178. package/src/shared/uuid.js +13 -0
  179. package/src/shared/wix.js +4 -3
  180. package/src/shared/zoho-books.js +3 -4
  181. package/src/shared/zoho-crm.js +1 -1
  182. package/src/shared/{zoho-regions.js → zoho.js} +4 -1
  183. package/test/ai.test.js +1 -1
  184. package/test/brevo.test.js +1 -1
  185. package/test/chargebee.test.js +1 -1
  186. package/test/google-ads.test.js +8 -4
  187. package/test/linkedin.test.js +1 -1
  188. package/test/resend.test.js +2 -2
  189. package/test/salesforce.test.js +1 -1
  190. package/test/snowflake.test.js +7 -7
  191. package/test/tiktok.test.js +1 -1
  192. package/test/wiz.test.js +1 -1
package/src/server/wix.js CHANGED
@@ -1,58 +1,21 @@
1
1
  import { ConnectorError } from "@jskit-ai/connectors-core/server";
2
2
  import { createSchema } from "json-rest-schema";
3
3
  import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
4
+ import { requestPath, sameRecord, cursor, querySchema, contactIdField } from "./wixValues.js";
4
5
  import { wixDefinition } from "../shared/wix.js";
6
+ import { wixBookingOperations } from "./wixBookings.js";
7
+ import { wixCmsOperations } from "./wixCms.js";
8
+ import { wixStoreOperations } from "./wixStores.js";
9
+ import { isUuid } from "../shared/uuid.js";
10
+ import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
5
11
 
6
- // Preserve native Wix JSON without silently dropping unsupported values.
7
- function serializeNativeJson(value) {
8
- return JSON.stringify(value, (key, item) => {
9
- if (["undefined", "function", "symbol", "bigint"].includes(typeof item) ||
10
- typeof item === "number" && !Number.isFinite(item)) throw new Error("JSON required");
11
- return item;
12
- });
13
- }
14
-
15
- const guid = (value) => typeof value === "string" && /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/iu.test(value);
16
- // Results are checked against the operation's own request; a different record is reported with what Wix got wrong.
17
- const requestPath = (request, index) => decodeURIComponent(new URL(request.url).pathname.split("/").at(index));
18
- function sameRecord(matches, message, codes) {
19
- if (!matches) throw new ConnectorError("connector_response_invalid", message, { statusCode: 502, ...codes });
20
- return true;
21
- }
22
- const cursor = (value) => typeof value === "string" && value.length > 0 && value.length <= 16000 && !/[\p{Cc}]/u.test(value);
23
- const querySchema = createSchema({
24
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
25
- cursor: { type: "string", minLength: 1, maxLength: 16000, noTrim: true, validator: (value) => cursor(value) || "Use the returned Wix page cursor." }
26
- });
12
+ // The Wix provider: its API-key headers, site addressing and the site and contact
13
+ // operations. The Bookings, eCommerce and Stores, and CMS operations live beside it.
27
14
  const contactsSchema = createSchema({
28
15
  limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
29
16
  offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
30
17
  search: { type: "string", minLength: 1, maxLength: 100 }
31
18
  });
32
- function bookingDirectoryOperation(kind) {
33
- const staff = kind === "staff";
34
- return {
35
- scopes: [],
36
- request(input) {
37
- const cursorPaging = validateSchemaPayload({ schema: querySchema, mode: "replace" }, input, { statusCode: 422 });
38
- return { method: "POST", url: staff ? "https://www.wixapis.com/bookings/v1/staff-members/query" : "https://www.wixapis.com/bookings/v2/categories/query",
39
- body: {
40
- query: { cursorPaging, sort: [{ fieldName: "id", order: "ASC" }], ...(staff ? { filter: { serviceProvider: true } } : {}) },
41
- ...(staff ? { fields: ["RESOURCE_DETAILS"] } : {})
42
- } };
43
- },
44
- validateResult(value) {
45
- const values = staff ? value?.staffMembers : value?.categories;
46
- const paging = value?.pagingMetadata;
47
- return Array.isArray(values) && values.length <= 100 && values.every(item => guid(item?.id) && typeof item.name === "string" &&
48
- (!staff || guid(item.resourceId))) && (paging === undefined || paging && typeof paging === "object" && !Array.isArray(paging) &&
49
- (paging.hasNext === undefined || typeof paging.hasNext === "boolean") &&
50
- (paging.cursors === undefined || paging.cursors && typeof paging.cursors === "object" && !Array.isArray(paging.cursors) &&
51
- ["next", "prev"].every(key => paging.cursors[key] === undefined || cursor(paging.cursors[key]))));
52
- }
53
- };
54
- }
55
- const contactIdField = { type: "string", required: true, validator: value => guid(value) || "Use the contact ID returned by Wix." };
56
19
  const contactInfoSchema = createSchema({
57
20
  name: { type: "object", schema: createSchema({ first: { type: "string", maxLength: 1000 }, last: { type: "string", maxLength: 1000 } }) },
58
21
  company: { type: "string", maxLength: 1000 }, jobTitle: { type: "string", maxLength: 1000 },
@@ -63,8 +26,8 @@ const contactInfoSchema = createSchema({
63
26
  items: { type: "object", schema: createSchema({ phone: { type: "string", required: true, minLength: 1, maxLength: 50 },
64
27
  countryCode: { type: "string", validator: value => /^[A-Z]{2}$/u.test(value) || "Use a two-letter country code." }, primary: { type: "boolean" } }) } } }) }
65
28
  });
66
- const validContact = value => guid(value?.id) && Number.isSafeInteger(value.revision) && value.revision >= 0 &&
67
- value.info && typeof value.info === "object" && !Array.isArray(value.info);
29
+ const validContact = value => isUuid(value?.id) && Number.isSafeInteger(value.revision) && value.revision >= 0 &&
30
+ isRecord(value.info);
68
31
  function contactOperation(action) {
69
32
  const fields = action === "get" ? { contactId: contactIdField } : {
70
33
  ...(action === "update" ? { contactId: contactIdField, revision: { type: "integer", required: true, min: 0, max: 2147483647 } } : {}),
@@ -87,442 +50,6 @@ function contactOperation(action) {
87
50
  sameRecord(value.contact.id === requestPath(request, -1), "Wix returned a different contact. Check the operation outcome before retrying.", { messageCode: "connectors.wix.returnedDifferentContactCheckOperation" }))
88
51
  };
89
52
  }
90
- const servicePagingSchema = createSchema({ limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 } });
91
- const serviceIdSchema = createSchema({ serviceId: contactIdField });
92
- const nativeJsonObjectField = { type: "none", validator: value => {
93
- if (!value || typeof value !== "object" || Array.isArray(value)) return "Use a native Wix JSON object.";
94
- try {
95
- const json = serializeNativeJson(value);
96
- return Buffer.byteLength(json) <= 100000 || "Keep native Wix object data within 100KB.";
97
- } catch { return "Use serializable native Wix JSON data."; }
98
- } };
99
- const serviceUpdateSchema = createSchema({
100
- serviceId: contactIdField,
101
- revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the returned service revision string." },
102
- name: { type: "string", minLength: 1, maxLength: 400 }, description: { type: "string", maxLength: 7000 }, tagLine: { type: "string", maxLength: 6000 },
103
- payment: nativeJsonObjectField,
104
- onlineBooking: { type: "object", schema: createSchema({ enabled: { type: "boolean" }, requireManualApproval: { type: "boolean" }, allowMultipleRequests: { type: "boolean" } }) },
105
- defaultCapacity: { type: "integer", min: 1, max: 1000 },
106
- staffMemberIds: { type: "array", validator: values => values.length > 0 && values.length <= 100 || "Select 1–100 staff resource IDs.", items: { type: "string", validator: value => guid(value) || "Use a staff resource ID." } },
107
- schedule: { type: "object", schema: createSchema({ availabilityConstraints: { type: "object", required: true, schema: createSchema({
108
- sessionDurations: { type: "array", validator: values => values.length > 0 && values.length <= 50 || "Use 1–50 appointment durations.", items: { type: "integer", min: 1 } },
109
- timeBetweenSessions: { type: "integer", min: 0 }
110
- }) } }) }
111
- });
112
- const validService = value => guid(value?.id) && typeof value.name === "string" && typeof value.revision === "string" && /^\d{1,19}$/u.test(value.revision) &&
113
- ["APPOINTMENT", "CLASS", "COURSE"].includes(value.type);
114
- const localDate = value => typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/u.test(value) &&
115
- Number.isFinite(Date.parse(`${value}Z`)) && new Date(`${value}Z`).toISOString().slice(0, 19) === value;
116
- const validSlot = value => guid(value?.serviceId) && localDate(value.localStartDate) && localDate(value.localEndDate) &&
117
- value.localStartDate < value.localEndDate && typeof value.bookable === "boolean";
118
- const availabilityLocationSchema = createSchema({
119
- id: { type: "string", validator: value => guid(value) || "Use the Wix business location ID." },
120
- locationType: { type: "string", required: true, enum: ["BUSINESS", "CUSTOM", "CUSTOMER"] },
121
- name: { type: "string", maxLength: 1000 }, formattedAddress: { type: "string", maxLength: 4000 }
122
- });
123
- // Availability must answer for the requested service, local time window and time zone.
124
- function requestedAvailability(value, requested, events) {
125
- const slots = value.timeSlots ?? [value.timeSlot];
126
- sameRecord(!(requested.timeZone && value.timeZone !== requested.timeZone || slots.some(slot => requested.serviceId && slot.serviceId !== requested.serviceId ||
127
- requested.localStartDate && (slot.localStartDate !== requested.localStartDate || slot.localEndDate !== requested.localEndDate))),
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
- }
131
- function availabilityOperation(action, events = false) {
132
- const schema = createSchema({
133
- ...(!events ? {
134
- customerChoices: { type: "object", schema: createSchema({
135
- durationInMinutes: { type: "integer", min: 1, max: 57600 },
136
- addOnIds: { type: "array", validator: values => values.length > 0 && values.length <= 21 && new Set(values).size === values.length || "Select 1–21 distinct add-ons.",
137
- items: { type: "string", validator: value => guid(value) || "Use the add-on ID returned by Wix." } }
138
- }) },
139
- includeResourceTypeIds: { type: "array", validator: values => values.length <= 100 || "Choose at most 100 resource types.", items: { type: "string", validator: value => guid(value) || "Use a resource type ID." } },
140
- ...(action === "list" ? { locations: { type: "array", validator: values => values.length <= 5 || "Choose at most five locations.", items: { type: "object", schema: availabilityLocationSchema } } } : {
141
- location: { type: "object", schema: availabilityLocationSchema },
142
- resourceTypes: { type: "array", validator: values => values.length <= 8 || "Choose at most eight resource types.", items: { type: "object", schema: createSchema({
143
- resourceTypeId: { type: "string", required: true, validator: value => guid(value) || "Use a resource type ID." },
144
- resourceIds: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 135 || "Choose 1–135 resource IDs.", items: { type: "string", validator: value => guid(value) || "Use a resource ID." } }
145
- }) } }
146
- })
147
- } : {}),
148
- ...(events ? { minBookableCapacity: { type: "integer", min: 1, max: 1000 } } : {}),
149
- serviceId: { type: "string", validator: value => guid(value) || "Use the appointment service ID." },
150
- ...(action === "list" ? {
151
- fromLocalDate: { type: "string", validator: value => localDate(value) || "Use YYYY-MM-DDThh:mm:ss without an offset." },
152
- toLocalDate: { type: "string", validator: value => localDate(value) || "Use YYYY-MM-DDThh:mm:ss without an offset." },
153
- limit: { type: "integer", min: 1, max: 1000, defaultTo: 100 },
154
- cursor: { type: "string", validator: value => cursor(value) || "Use the returned page cursor." }
155
- } : {
156
- localStartDate: { type: "string", required: true, validator: value => localDate(value) || "Use the returned local start date." },
157
- localEndDate: { type: "string", required: true, validator: value => localDate(value) || "Use the returned local end date." }
158
- }),
159
- timeZone: { type: "string", maxLength: events ? 100 : 150, validator: value => {
160
- try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
161
- } }
162
- });
163
- return {
164
- scopes: [],
165
- request(input) {
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, 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
- 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, messageCode: "connectors.wix.chooseServiceTimeZoneIncreasing" });
171
- }
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, messageCode: "connectors.wix.chooseBusinessLocationId" });
174
- const { serviceId, ...eventBody } = body;
175
- return { method: "POST", url: `https://www.wixapis.com/_api/service-availability/v2/time-slots${events ? "/event" : action === "get" ? "/get" : ""}`,
176
- body: action === "get" ? body : next ? { cursorPaging: { limit, cursor: next } } : { ...(events ? { ...eventBody, serviceIds: [serviceId], includeNonBookable: false } : { ...body, bookable: true }), cursorPaging: { limit } } };
177
- },
178
- validateResult: (value, request) => typeof value?.timeZone === "string" && (action === "get" ? validSlot(value.timeSlot) :
179
- Array.isArray(value.timeSlots) && value.timeSlots.length <= 1000 && value.timeSlots.every(slot => validSlot(slot) && (!events || typeof slot.eventInfo?.eventId === "string" && slot.eventInfo.eventId.length >= 36 && slot.eventInfo.eventId.length <= 250)) &&
180
- (value[events ? "pagingMetadata" : "cursorPagingMetadata"] === undefined || value[events ? "pagingMetadata" : "cursorPagingMetadata"] && typeof value[events ? "pagingMetadata" : "cursorPagingMetadata"] === "object" && !Array.isArray(value[events ? "pagingMetadata" : "cursorPagingMetadata"]) &&
181
- (value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors === undefined || value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors && typeof value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors === "object" && !Array.isArray(value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors) &&
182
- ["next", "prev"].every(key => value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors[key] === undefined || cursor(value[events ? "pagingMetadata" : "cursorPagingMetadata"].cursors[key]))))) &&
183
- requestedAvailability(value, request.body, events)
184
- };
185
- }
186
- const eventSlotSchema = createSchema({
187
- eventId: { type: "string", required: true, minLength: 36, maxLength: 250, validator: value => !/[\p{Cc}]/u.test(value) || "Use the returned event ID." },
188
- timeZone: { type: "string", required: true, maxLength: 100, validator: value => {
189
- try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
190
- } }
191
- });
192
- function productReadOperation(version, action) {
193
- const schema = createSchema(action === "get" ? {
194
- productId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix product ID." }
195
- } : {
196
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
197
- ...(version === 1 ? { offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 }, includeVariants: { type: "boolean", defaultTo: false } } :
198
- { cursor: { type: "string", validator: value => cursor(value) || "Use the returned product cursor." } })
199
- });
200
- const validProduct = value => guid(value?.id) && typeof value.name === "string" && value.name.length > 0;
201
- return {
202
- scopes: [],
203
- request(input) {
204
- const value = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
205
- const base = version === 1 ? "https://www.wixapis.com/stores-reader/v1/products" : "https://www.wixapis.com/stores/v3/products";
206
- if (action === "get") return { method: "GET", url: `${base}/${value.productId}` };
207
- const { includeVariants, ...paging } = value;
208
- return { method: "POST", url: `${base}/query`,
209
- body: version === 1 ? { query: { paging }, includeVariants } : { query: { cursorPaging: paging } } };
210
- },
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
- (version === 1 ? (value.totalResults === undefined || Number.isSafeInteger(value.totalResults) && value.totalResults >= 0) &&
213
- (value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
214
- ["items", "offset"].every(key => value.metadata[key] === undefined || Number.isSafeInteger(value.metadata[key]) && value.metadata[key] >= 0)) :
215
- (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
216
- (value.pagingMetadata.cursors === undefined || value.pagingMetadata.cursors && typeof value.pagingMetadata.cursors === "object" && !Array.isArray(value.pagingMetadata.cursors) &&
217
- ["next", "prev"].every(key => value.pagingMetadata.cursors[key] === undefined || cursor(value.pagingMetadata.cursors[key])))))
218
- };
219
- }
220
- const collectionId = value => typeof value === "string" && value.length > 0 && value.length <= 256 &&
221
- !/[\p{Cc}]/u.test(value) && value.split("/").every(part => part.length > 0 && ![".", ".."].includes(part));
222
- const collectionListSchema = createSchema({
223
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
224
- consistentRead: { type: "boolean", defaultTo: false }
225
- });
226
- const cmsFilterField = { type: "none", validator: value => {
227
- if (!value || typeof value !== "object" || Array.isArray(value)) return "Use a Wix filter object.";
228
- try {
229
- const json = serializeNativeJson(value);
230
- return Buffer.byteLength(json) <= 100000 || "Keep the Wix filter within 100KB.";
231
- } catch { return "Use serializable JSON filter data."; }
232
- } };
233
- const cmsItemQuerySchema = createSchema({
234
- collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
235
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
236
- consistentRead: { type: "boolean", defaultTo: false },
237
- filter: cmsFilterField,
238
- sort: { type: "array", validator: values => values.length <= 100 || "Use at most 100 sort fields.", items: { type: "object", schema: createSchema({
239
- fieldName: { type: "string", required: true, minLength: 1, maxLength: 1000 }, order: { type: "string", required: true, enum: ["ASC", "DESC"] }
240
- }) } },
241
- fields: { type: "array", validator: values => values.length <= 1000 || "Use at most 1000 projected fields.", items: { type: "string", minLength: 1, maxLength: 5000 } },
242
- returnTotalCount: { type: "boolean" },
243
- includeReferences: { type: "array", validator: values => values.length <= 100 || "Use at most 100 reference configurations.", items: { type: "object", schema: createSchema({
244
- field: { type: "string", required: true, minLength: 1, maxLength: 1000 }, limit: { type: "integer", min: 1, max: 1000 }
245
- }) } }
246
- });
247
- const cmsItemIdField = { type: "string", required: true, minLength: 1, maxLength: 128,
248
- validator: value => !/[\p{Cc}]/u.test(value) && ![".", ".."].includes(value) || "Use a returned Wix item ID." };
249
- const cmsItemGetSchema = createSchema({
250
- collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
251
- itemId: cmsItemIdField, consistentRead: { type: "boolean", defaultTo: false }
252
- });
253
- const cmsItemPatchSchema = createSchema({
254
- collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
255
- itemId: cmsItemIdField, conditionFilter: cmsFilterField,
256
- fieldModifications: { type: "none", required: true, validator: values => {
257
- if (!Array.isArray(values) || values.length < 1 || values.length > 100) return "Provide 1–100 Wix field modifications.";
258
- const options = { SET_FIELD: "setFieldOptions", INCREMENT_FIELD: "incrementFieldOptions", APPEND_TO_ARRAY: "appendToArrayOptions", REMOVE_FROM_ARRAY: "removeFromArrayOptions" };
259
- for (const value of values) {
260
- if (!value || typeof value !== "object" || Array.isArray(value) || typeof value.fieldPath !== "string" ||
261
- !value.fieldPath.length || value.fieldPath.length > 1000 || /[\p{Cc}]/u.test(value.fieldPath)) return "Use a Wix field path for each modification.";
262
- if (value.action === "REMOVE_FIELD") {
263
- if (Object.keys(value).some(key => !["fieldPath", "action"].includes(key))) return "REMOVE_FIELD accepts no value options.";
264
- } else {
265
- const key = Object.hasOwn(options, value.action) ? options[value.action] : undefined;
266
- if (!key || Object.keys(value).some(name => !["fieldPath", "action", key].includes(name))) return "Use the options matching the Wix patch action.";
267
- const config = value[key];
268
- if (!config || typeof config !== "object" || Array.isArray(config) || !Object.hasOwn(config, "value") || Object.keys(config).length !== 1) return "Provide the patch action's value.";
269
- if (value.action === "INCREMENT_FIELD" && (typeof config.value !== "number" || !Number.isFinite(config.value))) return "Use a finite increment value.";
270
- }
271
- }
272
- try {
273
- const json = serializeNativeJson(values);
274
- return Buffer.byteLength(json) <= 100000 || "Keep field modifications within 100KB.";
275
- } catch { return "Use serializable JSON field values."; }
276
- } }
277
- });
278
- const collectionGetSchema = createSchema({
279
- collectionId: { type: "string", required: true, validator: value => collectionId(value) || "Use the returned Wix collection ID." },
280
- consistentRead: { type: "boolean", defaultTo: false }
281
- });
282
- const validCollection = value => collectionId(value?.id) &&
283
- (value.fields === undefined || Array.isArray(value.fields)) && (value.displayName === undefined || typeof value.displayName === "string");
284
- const serviceLocationSchema = createSchema({
285
- type: { type: "string", required: true, enum: ["BUSINESS", "CUSTOM", "CUSTOMER"] },
286
- business: { type: "object", schema: createSchema({ id: contactIdField }) },
287
- custom: { type: "object", schema: createSchema({ address: { ...nativeJsonObjectField, required: true } }) }
288
- });
289
- const setServiceLocationsSchema = createSchema({
290
- serviceId: contactIdField,
291
- locations: { type: "array", required: true, validator: values => values.length <= 100 || "Use at most 100 locations.", items: { type: "object", schema: serviceLocationSchema } },
292
- removedLocationAction: { type: "string", required: true, enum: ["KEEP_AT_CURRENT_LOCATION", "MOVE_TO_LOCATION"] },
293
- moveToLocation: { type: "object", schema: serviceLocationSchema },
294
- notifyParticipants: { type: "boolean", defaultTo: false }, notificationMessage: { type: "string", maxLength: 2000 }
295
- });
296
- const serviceCreateSchema = createSchema({ service: { ...nativeJsonObjectField, required: true } });
297
- function locationWriteOperation(action) {
298
- const schema = createSchema({
299
- ...(action === "update" ? {
300
- locationId: contactIdField,
301
- revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current location revision string." }
302
- } : {}),
303
- name: { type: "string", required: true, minLength: 1, maxLength: 150 },
304
- timeZone: { type: "string", required: true, maxLength: 150, validator: value => {
305
- try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
306
- } },
307
- description: { type: "string", maxLength: 500 }, email: { type: "string", maxLength: 320 },
308
- phone: { type: "string", maxLength: 100 }, fax: { type: "string", maxLength: 100 },
309
- status: { type: "string", enum: ["ACTIVE"] },
310
- locationTypes: { type: "array", validator: values => values.length <= 10 || "Use at most 10 location types.", items: { type: "string", enum: ["UNKNOWN", "BRANCH", "OFFICES", "RECEPTION", "HEADQUARTERS", "INVENTORY"] } },
311
- businessSchedule: nativeJsonObjectField, extendedFields: nativeJsonObjectField,
312
- address: { type: "object", required: true, schema: createSchema({
313
- country: { type: "string", validator: value => /^[A-Z]{2}$/u.test(value) || "Use a two-letter country code." },
314
- subdivision: { type: "string", maxLength: 100 }, city: { type: "string", maxLength: 1000 }, postalCode: { type: "string", maxLength: 20 },
315
- formattedAddress: { type: "string", maxLength: 4000 }, hint: { type: "string", maxLength: 500 },
316
- streetAddress: { type: "object", schema: createSchema({ number: { type: "string", maxLength: 100 }, name: { type: "string", maxLength: 1000 }, apt: { type: "string", maxLength: 100 } }) },
317
- geocode: { type: "object", schema: createSchema({ latitude: { type: "number", required: true, min: -90, max: 90 }, longitude: { type: "number", required: true, min: -180, max: 180 } }) }
318
- }) }
319
- });
320
- return {
321
- scopes: [],
322
- request(input) {
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, messageCode: "connectors.wix.provideLocationAddress" });
325
- if (location.businessSchedule && (["periods", "specialHourPeriod"].some(key => location.businessSchedule[key] !== undefined &&
326
- (!Array.isArray(location.businessSchedule[key]) || location.businessSchedule[key].length > 100)) ||
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, messageCode: "connectors.wix.businessScheduleLimits" });
329
- }
330
- return { method: action === "create" ? "POST" : "PUT", url: `https://www.wixapis.com/locations/v1/locations${locationId ? `/${locationId}` : ""}`,
331
- body: { location: { ...(locationId ? { id: locationId } : {}), ...location } } };
332
- },
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.", { messageCode: "connectors.wix.returnedDifferentBusinessLocation" }))
335
- };
336
- }
337
- function locationActionOperation(action) {
338
- const schema = createSchema({ locationId: contactIdField });
339
- return {
340
- scopes: [],
341
- request(input) {
342
- const { locationId } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
343
- return { method: "POST", url: `https://www.wixapis.com/locations/v1/locations/${locationId}/${action}`,
344
- body: {} };
345
- },
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
- (action === "archive" ? value.location.archived === true : value.location.default === true)
348
- };
349
- }
350
- const locationQuerySchema = createSchema({
351
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 }, offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 },
352
- archived: { type: "boolean", defaultTo: false }
353
- });
354
- const locationGetSchema = createSchema({ locationId: { type: "string", required: true, validator: value => guid(value) || "Use the returned location ID." } });
355
- const validLocation = value => guid(value?.id) && typeof value.name === "string" &&
356
- (value.timeZone === undefined || typeof value.timeZone === "string") && (value.archived === undefined || typeof value.archived === "boolean");
357
- const productV1UpdateSchema = createSchema({
358
- productId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix product ID." },
359
- name: { type: "string", minLength: 1, maxLength: 80 }, visible: { type: "boolean" },
360
- price: { type: "none", validator: value => typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 999999999.99 || "Use a numeric Wix base price from 0 to 999999999.99." }
361
- });
362
- const productV3UpdateSchema = createSchema({
363
- productId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix product ID." },
364
- revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current product revision string." },
365
- name: { type: "string", minLength: 1, maxLength: 80 },
366
- visible: { type: "boolean" }
367
- });
368
- const catalogVersionSchema = createSchema({});
369
- const orderSearchSchema = createSchema({
370
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
371
- cursor: { type: "string", validator: value => cursor(value) || "Use the returned order cursor." },
372
- status: { type: "string", minLength: 1, maxLength: 100 },
373
- contactId: { type: "string", validator: value => guid(value) || "Use the buyer's contact ID." }
374
- });
375
- const orderIdSchema = createSchema({ orderId: { type: "string", required: true, minLength: 1, maxLength: 100, validator: value => !/[\p{Cc}]/u.test(value) && ![".", ".."].includes(value) || "Use the returned order ID." } });
376
- const cartCreateSchema = createSchema({
377
- note: { type: "string", maxLength: 1000 },
378
- catalogItems: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 300 || "Select 1–300 catalogue items.",
379
- items: { type: "object", schema: createSchema({
380
- quantity: { type: "integer", required: true, min: 1, max: 100000 },
381
- catalogReference: { type: "object", required: true, schema: createSchema({
382
- appId: { type: "string", required: true, validator: value => guid(value) || "Use the catalogue app ID." },
383
- catalogItemId: { type: "string", required: true, minLength: 1, maxLength: 36, validator: value => !/[\p{Cc}]/u.test(value) || "Use the selected catalogue item ID." },
384
- options: nativeJsonObjectField
385
- }) }
386
- }) } }
387
- });
388
- const cartIdSchema = createSchema({ cartId: { type: "string", required: true, validator: value => guid(value) || "Use the cart ID returned by Wix." } });
389
- const validOrder = value => typeof value?.id === "string" && value.id.length > 0 && value.id.length <= 100 &&
390
- typeof value.status === "string" && value.status.length > 0 && (value.currency === undefined || typeof value.currency === "string") &&
391
- (value.lineItems === undefined || Array.isArray(value.lineItems));
392
- const formSummarySchema = createSchema({ formId: { type: "string", required: true, validator: value => guid(value) || "Use the service form ID." } });
393
- const bookingCreateSchema = createSchema({
394
- serviceId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix service ID." },
395
- kind: { type: "string", required: true, enum: ["APPOINTMENT", "CLASS", "COURSE"] },
396
- timeZone: { type: "string", required: true, maxLength: 150, validator: value => {
397
- try { new Intl.DateTimeFormat("en", { timeZone: value }); return true; } catch { return "Use an IANA time zone."; }
398
- } },
399
- startDate: { type: "string", validator: value => localDate(value) || "Use the selected slot's local start date." },
400
- endDate: { type: "string", validator: value => localDate(value) || "Use the selected slot's local end date." },
401
- eventId: { type: "string", minLength: 36, maxLength: 250, validator: value => !/[\p{Cc}]/u.test(value) || "Use the returned event ID." },
402
- scheduleId: { type: "string", validator: value => guid(value) || "Use the course schedule ID." },
403
- resourceId: { type: "string", validator: value => guid(value) || "Use the selected resource ID." },
404
- location: { type: "object", schema: createSchema({
405
- id: { type: "string", validator: value => guid(value) || "Use the business location ID." },
406
- locationType: { type: "string", required: true, enum: ["OWNER_BUSINESS", "OWNER_CUSTOM", "CUSTOM"] },
407
- name: { type: "string", maxLength: 1000 }, formattedAddress: { type: "string", maxLength: 4000 }
408
- }) },
409
- totalParticipants: { type: "integer", min: 1, max: 1000 },
410
- participantsChoices: { type: "object", schema: createSchema({
411
- serviceChoices: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 20 || "Use 1–20 participant groups.",
412
- items: { type: "object", schema: createSchema({
413
- numberOfParticipants: { type: "integer", required: true, min: 1, max: 1000 },
414
- choices: { type: "array", required: true, validator: values => values.length > 0 && values.length <= 5 || "Use 1–5 service choices per group.",
415
- items: { type: "object", schema: createSchema({
416
- optionId: { type: "string", required: true, validator: value => guid(value) || "Use the option ID returned by Wix." },
417
- custom: { type: "string", minLength: 1, maxLength: 1000 },
418
- duration: { type: "object", schema: createSchema({ minutes: { type: "integer", required: true, min: 1, max: 44639 }, name: { type: "string", maxLength: 255 } }) }
419
- }) } }
420
- }) } }
421
- }) },
422
- bookedAddOns: { type: "array", validator: values => values.length > 0 && values.length <= 21 && new Set(values.map(value => value?.id)).size === values.length || "Select 1–21 distinct add-ons.",
423
- items: { type: "object", schema: createSchema({
424
- id: { type: "string", required: true, validator: value => guid(value) || "Use the add-on ID returned by Wix." },
425
- groupId: { type: "string", validator: value => guid(value) || "Use the add-on group ID." },
426
- quantity: { type: "integer", min: 1, max: 1000 }
427
- }) } },
428
- formSubmission: { type: "none", required: true, validator: value => {
429
- if (!value || typeof value !== "object" || Array.isArray(value)) return "Provide the booking form's field-value object.";
430
- try {
431
- const json = serializeNativeJson(value);
432
- return Buffer.byteLength(json) <= 100000 || "Keep booking form data within 100KB.";
433
- } catch { return "Use serializable JSON booking form data."; }
434
- } },
435
- notifyParticipants: { type: "boolean", defaultTo: false }, sendSmsReminder: { type: "boolean", defaultTo: false }
436
- });
437
- const bookingQuerySchema = createSchema({
438
- bookingId: { type: "string", validator: value => guid(value) || "Use the Wix booking ID." },
439
- limit: { type: "integer", min: 1, max: 100, defaultTo: 20 },
440
- offset: { type: "integer", min: 0, max: 100000, defaultTo: 0 }
441
- });
442
- const bookingCancelSchema = createSchema({
443
- bookingId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix booking ID." },
444
- revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current booking revision string." },
445
- notifyParticipants: { type: "boolean", defaultTo: false },
446
- message: { type: "string", maxLength: 2000 },
447
- waiveCharges: { type: "boolean", defaultTo: false }
448
- });
449
- const bookingRescheduleSchema = createSchema({
450
- bookingId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix booking ID." },
451
- revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current booking revision string." },
452
- slot: { ...nativeJsonObjectField, required: true },
453
- notifyParticipants: { type: "boolean", defaultTo: false }, message: { type: "string", maxLength: 2000 }
454
- });
455
- function bookingConfirmationOperation(action) {
456
- const automatic = action === "confirmOrDecline";
457
- const schema = createSchema({
458
- bookingId: { type: "string", required: true, validator: value => guid(value) || "Use the Wix booking ID." },
459
- ...(automatic ? {
460
- paymentStatus: { type: "string", required: true, enum: ["UNDEFINED", "NOT_PAID", "PAID", "PARTIALLY_PAID", "REFUNDED", "EXEMPT"] }
461
- } : {
462
- revision: { type: "none", required: true, validator: value => typeof value === "string" && /^\d{1,19}$/u.test(value) && BigInt(value) <= 9223372036854775807n || "Use the current booking revision string." },
463
- notifyParticipants: { type: "boolean", defaultTo: false }, message: { type: "string", maxLength: 2000 }
464
- })
465
- });
466
- return {
467
- scopes: [],
468
- request(input) {
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, messageCode: "connectors.wix.enableParticipantNotificationSupplyingMessage" });
471
- return { method: "POST", url: automatic ? `https://www.wixapis.com/bookings/v2/confirmation/${bookingId}:confirmOrDecline` :
472
- `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/${action}`,
473
- body: automatic ? { paymentStatus } : { revision, participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
474
- },
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.", { messageCode: "connectors.wix.returnedDifferentBookingCheckOperation" }) &&
477
- typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
478
- BigInt(value.booking.revision) <= 9223372036854775807n && (automatic ? ["CONFIRMED", "PENDING", "DECLINED"].includes(value.booking.status) : value.booking.status === (action === "confirm" ? "CONFIRMED" : "DECLINED"))
479
- };
480
- }
481
- function fulfillmentWriteOperation(update) {
482
- const trackingSchema = createSchema({
483
- trackingNumber: { type: "string", minLength: 1, maxLength: 100 },
484
- shippingProvider: { type: "string", minLength: 1, maxLength: 100 },
485
- trackingLink: { type: "string", minLength: 1, maxLength: 2048, validator: value => {
486
- try { const url = new URL(value); return ["https:", "http:"].includes(url.protocol) && !url.username && !url.password || "Use a public tracking URL."; }
487
- catch { return "Use a public tracking URL."; }
488
- } }
489
- });
490
- const schema = createSchema({
491
- orderId: { type: "string", required: true, minLength: 1, maxLength: 100, validator: value => !/[\p{Cc}]/u.test(value) && ![".", ".."].includes(value) || "Use the returned order ID." },
492
- ...(update ? { fulfillmentId: contactIdField } : {}),
493
- lineItems: { type: "array", required: !update, validator: values => values.length > 0 && values.length <= 300 && new Set(values.map(item => item?.id)).size === values.length || "Select 1–300 distinct order line items.",
494
- items: { type: "object", schema: createSchema({ id: contactIdField, quantity: { type: "integer", required: true, min: 1, max: 100000 } }) } },
495
- trackingInfo: { type: "object", schema: trackingSchema },
496
- status: { type: "string", enum: ["Pending", "Accepted", "Ready", "In_Delivery", "Fulfilled"] },
497
- completed: { type: "boolean" }
498
- });
499
- return {
500
- scopes: [],
501
- request(input) {
502
- const { orderId, fulfillmentId, ...fulfillment } = validateSchemaPayload({ schema, mode: "replace" }, input, { statusCode: 422 });
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, messageCode: "connectors.wix.supplyFulfillmentPropertiesWrite" });
505
- }
506
- const tracking = fulfillment.trackingInfo;
507
- if (!update && tracking && (!tracking.trackingNumber || !tracking.shippingProvider ||
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, messageCode: "connectors.wix.supplyTrackingNumberProviderCustom" });
510
- }
511
- return { method: update ? "PATCH" : "POST", url: update
512
- ? `https://www.wixapis.com/ecom/v1/fulfillments/${fulfillmentId}/orders/${encodeURIComponent(orderId)}`
513
- : `https://www.wixapis.com/ecom/v1/fulfillments/orders/${encodeURIComponent(orderId)}/create-fulfillment`,
514
- body: { fulfillment } };
515
- },
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.", { messageCode: "connectors.wix.returnedFulfillmentsDifferentOrder" }) &&
518
- Array.isArray(value.orderWithFulfillments.fulfillments) &&
519
- value.orderWithFulfillments.fulfillments.every(item => guid(item?.id) && Array.isArray(item.lineItems) && item.lineItems.length <= 300 &&
520
- item.lineItems.every(line => guid(line?.id) && Number.isInteger(line.quantity) && line.quantity >= 1 && line.quantity <= 100000)) &&
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.", { messageCode: "connectors.wix.didNotReturnUpdatedFulfillment" }) :
523
- guid(value.fulfillmentId) && value.orderWithFulfillments.fulfillments.some(item => item.id === value.fulfillmentId))
524
- };
525
- }
526
53
  const wixRuntime = {
527
54
  ...wixDefinition, apiOrigins: ["https://www.wixapis.com"],
528
55
  apiKey: { headers: (key, settings) => ({ Authorization: key, "wix-account-id": settings.accountId }) },
@@ -534,394 +61,9 @@ const wixRuntime = {
534
61
  return request(address, { ...options, headers });
535
62
  },
536
63
  operations: {
537
- "carts.create": {
538
- scopes: [],
539
- request(input) {
540
- const { note, catalogItems } = validateSchemaPayload({ schema: cartCreateSchema, mode: "replace" }, input, { statusCode: 422 });
541
- return { method: "POST", url: "https://www.wixapis.com/ecom/v2/carts",
542
- body: { cart: { source: { channelType: "WEB" }, ...(note === undefined ? {} : { note }) }, catalogItems } };
543
- },
544
- validateResult: value => guid(value?.cart?.id) && Array.isArray(value.cart.lineItems) && value.cart.lineItems.length <= 300 &&
545
- value.cart.lineItems.every(item => guid(item?.id))
546
- },
547
- "carts.getCheckoutUrl": {
548
- scopes: [],
549
- request(input) {
550
- const { cartId } = validateSchemaPayload({ schema: cartIdSchema, mode: "replace" }, input, { statusCode: 422 });
551
- return { method: "POST", url: `https://www.wixapis.com/ecom/v2/carts/${cartId}/get-checkout-url`, body: {} };
552
- },
553
- validateResult(value) {
554
- if (typeof value?.checkoutUrl !== "string" || value.checkoutUrl.length > 8000 || /[\p{Cc}]/u.test(value.checkoutUrl)) return false;
555
- try { const url = new URL(value.checkoutUrl); return url.protocol === "https:" && !url.username && !url.password; } catch { return false; }
556
- }
557
- },
558
- "bookings.reschedule": {
559
- scopes: [],
560
- request(input) {
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, messageCode: "connectors.wix.enableParticipantNotificationSupplyingMessage" });
563
- if (slot.eventId !== undefined) {
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, messageCode: "connectors.wix.classSupplySelectedEventidSlot" });
566
- }
567
- } else {
568
- let validTimeZone = false;
569
- try { validTimeZone = typeof slot.timezone === "string" && !!new Intl.DateTimeFormat("en", { timeZone: slot.timezone }); } catch {}
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, messageCode: "connectors.wix.appointmentSupplyServiceScheduleIds" });
572
- }
573
- }
574
- return { method: "POST", url: `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/reschedule`,
575
- body: { revision, slot, participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
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.", { messageCode: "connectors.wix.returnedDifferentBookingCheckOperation" }) && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
578
- BigInt(value.booking.revision) <= 9223372036854775807n && typeof value.booking.status === "string" && value.booking.status.length > 0
579
- },
580
- "bookings.confirm": bookingConfirmationOperation("confirm"),
581
- "bookings.decline": bookingConfirmationOperation("decline"),
582
- "bookings.confirmOrDecline": bookingConfirmationOperation("confirmOrDecline"),
583
- "bookingServices.listAddOnGroups": {
584
- scopes: [],
585
- request(input) {
586
- const value = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
587
- return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services/add-on-groups/list-add-on-groups-by-service-id",
588
- body: value };
589
- },
590
- validateResult: value => Array.isArray(value?.addOnGroupsDetails) && value.addOnGroupsDetails.length <= 3 &&
591
- value.addOnGroupsDetails.every(group => guid(group?.groupId) && typeof group.groupName === "string" &&
592
- Number.isInteger(group.maxNumberOfAddOns) && group.maxNumberOfAddOns >= 0 &&
593
- Array.isArray(group.addOns) && group.addOns.length <= 7 && group.addOns.every(addOn => guid(addOn?.addOnId) && typeof addOn.name === "string"))
594
- },
595
- "bookingServices.getVariants": {
596
- scopes: [],
597
- request(input) {
598
- const { serviceId } = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
599
- return { method: "GET", url: `https://www.wixapis.com/bookings/v1/serviceOptionsAndVariants/service_id/${serviceId}` };
600
- },
601
- validateResult(value, request) {
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.", { messageCode: "connectors.wix.returnedVariantsDifferentService" }) &&
604
- Array.isArray(variants.options?.values) && variants.options.values.every(option => guid(option?.id) && typeof option.type === "string") &&
605
- Array.isArray(variants.variants?.values) && variants.variants.values.every(variant =>
606
- Array.isArray(variant?.choices) && variant.choices.every(choice => guid(choice?.optionId)));
607
- }
608
- },
609
- "bookingCategories.list": bookingDirectoryOperation("categories"),
610
- "bookingStaff.list": bookingDirectoryOperation("staff"),
611
- "bookingServices.setLocations": {
612
- scopes: [],
613
- request(input) {
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, 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
- for (const location of [...locations, ...(moveToLocation ? [moveToLocation] : [])]) {
618
- if (location.type === "BUSINESS" && (!location.business || location.custom) ||
619
- location.type === "CUSTOM" && (!location.custom || location.business || !Object.keys(location.custom.address).length) ||
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, messageCode: "connectors.wix.supplyDetailsMatchingServiceLocation" });
622
- }
623
- }
624
- return { method: "POST", url: `https://www.wixapis.com/_api/bookings/v2/services/${serviceId}/locations`, body: {
625
- locations, removedLocationSessionsAction: { action, ...(moveToLocation ? { moveToLocationOptions: { newLocation: moveToLocation } } : {}) },
626
- participantNotification: { notifyParticipants, ...(notificationMessage !== undefined ? { message: notificationMessage } : {}) }
627
- } };
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.", { messageCode: "connectors.wix.returnedDifferentBookingServiceCheck" })
630
- },
631
- "bookingServices.create": {
632
- scopes: [],
633
- request(input) {
634
- const { service } = validateSchemaPayload({ schema: serviceCreateSchema, mode: "replace" }, input, { statusCode: 422 });
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
- 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.", { messageCode: "connectors.wix.provideExplicitOnlineBookingSettings" });
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.", { messageCode: "connectors.wix.useFixedVariedCustomNo" });
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.", { 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
- const priced = ["FIXED", "VARIED"].includes(payment.rateType);
647
- if ((options.online || options.deposit) && !priced) invalid("Online and deposit payments require fixed or varied pricing.", { messageCode: "connectors.wix.onlineDepositPaymentsRequireFixed" });
648
- const rate = payment.rateType === "FIXED" ? payment.fixed : payment.varied;
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.", { 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
- const durations = service.schedule?.availabilityConstraints?.sessionDurations;
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.", { 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
- return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services", body: { service } };
659
- },
660
- validateResult: value => validService(value?.service)
661
- },
662
- "cmsItems.get": {
663
- scopes: [],
664
- request(input) {
665
- const { collectionId: dataCollectionId, itemId, consistentRead } = validateSchemaPayload({ schema: cmsItemGetSchema, mode: "replace" }, input, { statusCode: 422 });
666
- const url = new URL(`https://www.wixapis.com/wix-data/v2/items/${encodeURIComponent(itemId)}`);
667
- url.searchParams.set("dataCollectionId", dataCollectionId); url.searchParams.set("consistentRead", String(consistentRead));
668
- return { method: "GET", url: url.href };
669
- },
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.", { messageCode: "connectors.wix.returnedDifferentCmsItemCollection" })
672
- },
673
- "cmsItems.patch": {
674
- scopes: [],
675
- request(input) {
676
- const { collectionId: dataCollectionId, itemId, fieldModifications, conditionFilter } = validateSchemaPayload({ schema: cmsItemPatchSchema, mode: "replace" }, input, { statusCode: 422 });
677
- return { method: "PATCH", url: `https://www.wixapis.com/wix-data/v2/items/${encodeURIComponent(itemId)}`,
678
- body: { dataCollectionId, patch: { dataItemId: itemId, fieldModifications }, ...(conditionFilter !== undefined ? { condition: { filter: conditionFilter } } : {}) } };
679
- },
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.", { messageCode: "connectors.wix.returnedDifferentCmsItemCollection" })
682
- },
683
- "cmsItems.list": {
684
- scopes: [],
685
- request(input) {
686
- const { collectionId: dataCollectionId, consistentRead, filter, sort, fields, returnTotalCount, includeReferences, ...paging } = validateSchemaPayload({ schema: cmsItemQuerySchema, mode: "replace" }, input, { statusCode: 422 });
687
- return { method: "POST", url: "https://www.wixapis.com/wix-data/v2/items/query",
688
- body: { dataCollectionId, consistentRead,
689
- ...(returnTotalCount !== undefined ? { returnTotalCount } : {}), ...(includeReferences !== undefined ? { includeReferences } : {}),
690
- query: { paging, ...(filter !== undefined ? { filter } : {}), ...(sort !== undefined ? { sort } : {}), ...(fields !== undefined ? { fields } : {}) } } };
691
- },
692
- validateResult: (value, request) => Array.isArray(value?.dataItems) && value.dataItems.length <= 100 && value.dataItems.every(item =>
693
- typeof item?.id === "string" && item.id.length > 0 && collectionId(item.dataCollectionId) && item.data && typeof item.data === "object" && !Array.isArray(item.data)) &&
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.", { messageCode: "connectors.wix.returnedItemsDifferentCmsCollection" })
696
- },
697
- "cmsCollections.list": {
698
- scopes: [],
699
- request(input) {
700
- const value = validateSchemaPayload({ schema: collectionListSchema, mode: "replace" }, input, { statusCode: 422 });
701
- const url = new URL("https://www.wixapis.com/wix-data/v2/collections");
702
- url.searchParams.set("paging.limit", String(value.limit)); url.searchParams.set("paging.offset", String(value.offset));
703
- url.searchParams.set("consistentRead", String(value.consistentRead));
704
- return { method: "GET", url: url.href };
705
- },
706
- validateResult: value => Array.isArray(value?.collections) && value.collections.length <= 100 && value.collections.every(validCollection) &&
707
- (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata))
708
- },
709
- "cmsCollections.get": {
710
- scopes: [],
711
- request(input) {
712
- const value = validateSchemaPayload({ schema: collectionGetSchema, mode: "replace" }, input, { statusCode: 422 });
713
- const id = value.collectionId.split("/").map(encodeURIComponent).join("/");
714
- return { method: "GET", url: `https://www.wixapis.com/wix-data/v2/collections/${id}?consistentRead=${value.consistentRead}` };
715
- },
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.", { messageCode: "connectors.wix.returnedDifferentCmsCollection" })
718
- },
719
- "locations.archive": locationActionOperation("archive"),
720
- "locations.setDefault": locationActionOperation("set-default"),
721
- "locations.create": locationWriteOperation("create"),
722
- "locations.update": locationWriteOperation("update"),
723
- "locations.list": {
724
- scopes: [],
725
- request(input) {
726
- const { archived, ...paging } = validateSchemaPayload({ schema: locationQuerySchema, mode: "replace" }, input, { statusCode: 422 });
727
- return { method: "POST", url: "https://www.wixapis.com/locations/v1/locations/query",
728
- body: { query: { filter: { archived }, paging }, filterAuthorizedLocationEntities: true } };
729
- },
730
- validateResult: value => Array.isArray(value?.locations) && value.locations.length <= 100 && value.locations.every(validLocation) &&
731
- (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
732
- ["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0))
733
- },
734
- "locations.get": {
735
- scopes: [],
736
- request(input) {
737
- const { locationId } = validateSchemaPayload({ schema: locationGetSchema, mode: "replace" }, input, { statusCode: 422 });
738
- return { method: "GET", url: `https://www.wixapis.com/locations/v1/locations/${locationId}` };
739
- },
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
- },
742
- "productsV1.list": productReadOperation(1, "list"),
743
- "productsV1.get": productReadOperation(1, "get"),
744
- "productsV1.update": {
745
- scopes: [],
746
- request(input) {
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, messageCode: "connectors.wix.provideProductNameVisibilityBase" });
749
- return { method: "PATCH", url: `https://www.wixapis.com/stores/v1/products/${productId}`,
750
- body: { product: { ...changes, ...(price === undefined ? {} : { priceData: { price } }) } } };
751
- },
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.", { messageCode: "connectors.wix.returnedDifferentProduct" })
754
- },
755
- "productsV3.list": productReadOperation(3, "list"),
756
- "productsV3.get": productReadOperation(3, "get"),
757
- "productsV3.update": {
758
- scopes: [],
759
- request(input) {
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, messageCode: "connectors.wix.provideProductNameVisibilityChange" });
762
- return { method: "PATCH", url: `https://www.wixapis.com/stores/v3/products/${productId}`,
763
- body: { product: { id: productId, revision, ...changes } } };
764
- },
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.", { messageCode: "connectors.wix.returnedDifferentProduct" }) &&
767
- typeof value.product.revision === "string" && /^\d{1,19}$/u.test(value.product.revision) && BigInt(value.product.revision) <= 9223372036854775807n
768
- },
769
- "catalog.version": {
770
- scopes: [],
771
- request(input) {
772
- validateSchemaPayload({ schema: catalogVersionSchema, mode: "replace" }, input, { statusCode: 422 });
773
- return { method: "GET", url: "https://www.wixapis.com/stores/v3/provision/version" };
774
- }, validateResult: value => ["V1_CATALOG", "V3_CATALOG", "STORES_NOT_INSTALLED"].includes(value?.catalogVersion)
775
- },
776
- "orders.list": {
777
- scopes: [],
778
- request(input) {
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, messageCode: "connectors.wix.useCursorLimitNextOrder" });
781
- const filter = { ...(status ? { status } : {}), ...(contactId ? { "buyerInfo.contactId": contactId } : {}) };
782
- return { method: "POST", url: "https://www.wixapis.com/ecom/v1/orders/search",
783
- body: { search: { cursorPaging, ...(Object.keys(filter).length ? { filter } : {}) } } };
784
- },
785
- validateResult: value => Array.isArray(value?.orders) && value.orders.length <= 100 && value.orders.every(validOrder) &&
786
- (value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
787
- (value.metadata.count === undefined || value.metadata.count === value.orders.length) &&
788
- (value.metadata.hasNext === undefined || typeof value.metadata.hasNext === "boolean") &&
789
- (value.metadata.cursors === undefined || value.metadata.cursors && typeof value.metadata.cursors === "object" && !Array.isArray(value.metadata.cursors) &&
790
- ["next", "prev"].every(key => value.metadata.cursors[key] === undefined || cursor(value.metadata.cursors[key]))))
791
- },
792
- "orders.get": {
793
- scopes: [],
794
- request(input) {
795
- const { orderId } = validateSchemaPayload({ schema: orderIdSchema, mode: "replace" }, input, { statusCode: 422 });
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.", { messageCode: "connectors.wix.returnedDifferentOrder" })
798
- },
799
- "orderFulfillments.create": fulfillmentWriteOperation(false),
800
- "orderFulfillments.update": fulfillmentWriteOperation(true),
801
- "orderFulfillments.list": {
802
- scopes: [],
803
- request(input) {
804
- const { orderId } = validateSchemaPayload({ schema: orderIdSchema, mode: "replace" }, input, { statusCode: 422 });
805
- return { method: "GET", url: `https://www.wixapis.com/ecom/v1/fulfillments/orders/${encodeURIComponent(orderId)}` };
806
- },
807
- validateResult: (value, request) => guid(value?.orderWithFulfillments?.orderId) &&
808
- sameRecord(value.orderWithFulfillments.orderId === requestPath(request, -1), "Wix returned fulfillments for a different order.", { messageCode: "connectors.wix.returnedFulfillmentsDifferentOrder" }) &&
809
- Array.isArray(value.orderWithFulfillments.fulfillments) &&
810
- value.orderWithFulfillments.fulfillments.every(item => guid(item?.id) && Array.isArray(item.lineItems) && item.lineItems.length <= 300 &&
811
- item.lineItems.every(line => guid(line?.id) && Number.isInteger(line.quantity) && line.quantity >= 1 && line.quantity <= 100000))
812
- },
813
- "classAvailability.list": availabilityOperation("list", true),
814
- "classAvailability.get": {
815
- scopes: [],
816
- request(input) {
817
- const { eventId, timeZone } = validateSchemaPayload({ schema: eventSlotSchema, mode: "replace" }, input, { statusCode: 422 });
818
- const url = new URL(`https://www.wixapis.com/_api/service-availability/v2/time-slots/event/${encodeURIComponent(eventId)}`);
819
- url.searchParams.set("timeZone", timeZone);
820
- return { method: "GET", url: url.href };
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.", { messageCode: "connectors.wix.returnedDifferentEventSlotTime" })
823
- },
824
- "forms.summary": {
825
- scopes: [],
826
- request(input) {
827
- const { formId } = validateSchemaPayload({ schema: formSummarySchema, mode: "replace" }, input, { statusCode: 422 });
828
- return { method: "GET", url: `https://www.wixapis.com/form-schema-service/v4/forms/${formId}/summary` };
829
- },
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
- Array.isArray(value.formSummary.fields) && value.formSummary.fields.length <= 500 &&
832
- value.formSummary.fields.every(field => guid(field?.id) && typeof field.target === "string" && field.target.length > 0 && field.target.length <= 200 &&
833
- typeof field.type === "string" && field.type.length > 0 && (field.label === undefined || typeof field.label === "string" && field.label.length <= 350) &&
834
- (field.deleted === undefined || typeof field.deleted === "boolean") && (field.options === undefined || Array.isArray(field.options) && field.options.length <= 400))
835
- },
836
- "bookings.create": {
837
- scopes: [],
838
- request(input) {
839
- const value = validateSchemaPayload({ schema: bookingCreateSchema, mode: "replace" }, input, { statusCode: 422 });
840
- const { kind, serviceId, timeZone, startDate, endDate, eventId, scheduleId, resourceId, location, totalParticipants, participantsChoices, bookedAddOns, formSubmission, notifyParticipants, sendSmsReminder } = value;
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, messageCode: "connectors.wix.useParticipantChoicesFixedParticipant" });
843
- if (participantsChoices.serviceChoices.reduce((total, group) => total + group.numberOfParticipants, 0) > 1000 ||
844
- participantsChoices.serviceChoices.some(group => new Set(group.choices.map(choice => choice.optionId)).size !== group.choices.length ||
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, messageCode: "connectors.wix.useMost1000ParticipantsDistinct" });
847
- }
848
- }
849
- if (kind === "APPOINTMENT" ? !startDate || !endDate || startDate >= endDate || !resourceId || !location || eventId || scheduleId :
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, messageCode: "connectors.wix.useAppointmentDatesResourceLocation" });
852
- }
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
- const bookedEntity = kind === "COURSE" ? { schedule: { scheduleId, serviceId, timezone: timeZone } } :
855
- { slot: { serviceId, timezone: timeZone, ...(kind === "CLASS" ? { eventId } : { startDate, endDate, resource: { id: resourceId }, location }) } };
856
- return { method: "POST", url: "https://www.wixapis.com/_api/bookings-service/v2/bookings",
857
- body: { booking: { bookedEntity, ...(participantsChoices ? { participantsChoices } : { totalParticipants: totalParticipants ?? 1 }),
858
- ...(bookedAddOns ? { bookedAddOns } : {}) }, formSubmission, participantNotification: { notifyParticipants }, sendSmsReminder } };
859
- },
860
- validateResult: value => guid(value?.booking?.id) && typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) &&
861
- BigInt(value.booking.revision) <= 9223372036854775807n && typeof value.booking.status === "string" && value.booking.status.length > 0
862
- },
863
- "availability.list": availabilityOperation("list"),
864
- "availability.get": availabilityOperation("get"),
865
- "bookings.list": {
866
- scopes: [],
867
- request(input) {
868
- const { bookingId, ...paging } = validateSchemaPayload({ schema: bookingQuerySchema, mode: "replace" }, input, { statusCode: 422 });
869
- return { method: "POST", url: "https://www.wixapis.com/_api/bookings-reader/v2/extended-bookings/query",
870
- body: { query: { paging, ...(bookingId ? { filter: { id: bookingId } } : {}) }, withBookingAllowedActions: true } };
871
- },
872
- validateResult: (value, request) => Array.isArray(value?.extendedBookings) && value.extendedBookings.length <= 100 && value.extendedBookings.every(record =>
873
- guid(record?.booking?.id) && typeof record.booking.revision === "string" && /^\d{1,19}$/u.test(record.booking.revision) && BigInt(record.booking.revision) <= 9223372036854775807n &&
874
- typeof record.booking.status === "string" && record.booking.status.length > 0 &&
875
- (record.allowedActions === undefined || record.allowedActions && typeof record.allowedActions === "object" && !Array.isArray(record.allowedActions) &&
876
- ["cancel", "reschedule"].every(key => record.allowedActions[key] === undefined || typeof record.allowedActions[key] === "boolean"))) &&
877
- (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
878
- ["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
879
- (value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.extendedBookings.length)) &&
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.", { messageCode: "connectors.wix.returnedUnexpectedBookingRequestedId" }))
882
- },
883
- "bookings.cancel": {
884
- scopes: [],
885
- request(input) {
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, messageCode: "connectors.wix.enableParticipantNotificationSendCancellation" });
888
- return { method: "POST", url: `https://www.wixapis.com/_api/bookings-service/v2/bookings/${bookingId}/cancel`,
889
- body: { revision, waiveCharges,
890
- participantNotification: { notifyParticipants, ...(message === undefined ? {} : { message }) } } };
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.", { messageCode: "connectors.wix.returnedDifferentBookingCheckOperation" }) && value.booking.status === "CANCELED" &&
893
- typeof value.booking.revision === "string" && /^\d{1,19}$/u.test(value.booking.revision) && BigInt(value.booking.revision) <= 9223372036854775807n
894
- },
895
- "bookingServices.list": {
896
- scopes: [],
897
- request(input) {
898
- const paging = validateSchemaPayload({ schema: servicePagingSchema, mode: "replace" }, input, { statusCode: 422 });
899
- return { method: "POST", url: "https://www.wixapis.com/_api/bookings/v2/services/query", body: { query: { paging } } };
900
- },
901
- validateResult: value => Array.isArray(value?.services) && value.services.length <= 100 && value.services.every(validService) &&
902
- (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
903
- ["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
904
- (value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.services.length))
905
- },
906
- "bookingServices.get": {
907
- scopes: [],
908
- request(input) {
909
- const { serviceId } = validateSchemaPayload({ schema: serviceIdSchema, mode: "replace" }, input, { statusCode: 422 });
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.", { messageCode: "connectors.wix.returnedDifferentBookingServiceCheck" })
912
- },
913
- "bookingServices.update": {
914
- scopes: [],
915
- request(input) {
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, messageCode: "connectors.wix.chooseServiceFieldUpdate" });
918
- if (["payment", "onlineBooking"].some(key => service[key] !== undefined && !Object.keys(service[key]).length) ||
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, messageCode: "connectors.wix.provideSelectedServiceSettingsInstead" });
921
- }
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.", { messageCode: "connectors.wix.returnedDifferentBookingServiceCheck" })
924
- },
64
+ ...wixStoreOperations,
65
+ ...wixBookingOperations,
66
+ ...wixCmsOperations,
925
67
  "contacts.get": contactOperation("get"),
926
68
  "contacts.create": contactOperation("create"),
927
69
  "contacts.update": contactOperation("update"),
@@ -933,8 +75,8 @@ const wixRuntime = {
933
75
  body: { query: { paging }, ...(search === undefined ? {} : { search }) } };
934
76
  },
935
77
  validateResult: value => Array.isArray(value?.contacts) && value.contacts.length <= 100 &&
936
- value.contacts.every(contact => guid(contact?.id) && (contact.info === undefined || contact.info && typeof contact.info === "object" && !Array.isArray(contact.info))) &&
937
- (value.pagingMetadata === undefined || value.pagingMetadata && typeof value.pagingMetadata === "object" && !Array.isArray(value.pagingMetadata) &&
78
+ value.contacts.every(contact => isUuid(contact?.id) && (contact.info === undefined || isRecord(contact.info))) &&
79
+ (value.pagingMetadata === undefined || isRecord(value.pagingMetadata) &&
938
80
  ["count", "offset", "total"].every(key => value.pagingMetadata[key] === undefined || Number.isSafeInteger(value.pagingMetadata[key]) && value.pagingMetadata[key] >= 0) &&
939
81
  (value.pagingMetadata.count === undefined || value.pagingMetadata.count === value.contacts.length))
940
82
  },
@@ -944,12 +86,12 @@ const wixRuntime = {
944
86
  const paging = validateSchemaPayload({ schema: querySchema, mode: "replace" }, input, { statusCode: 422 });
945
87
  return { method: "POST", url: "https://www.wixapis.com/site-list/v2/sites/query", body: { query: { cursorPaging: paging } } };
946
88
  },
947
- validateResult: (value) => Array.isArray(value?.sites) && value.sites.length <= 100 && value.sites.every((site) => guid(site?.id) &&
89
+ validateResult: (value) => Array.isArray(value?.sites) && value.sites.length <= 100 && value.sites.every((site) => isUuid(site?.id) &&
948
90
  ["name", "displayName", "viewUrl", "editUrl", "thumbnail"].every((key) => site[key] === undefined || typeof site[key] === "string") &&
949
91
  ["published", "premium", "domainConnected"].every((key) => site[key] === undefined || typeof site[key] === "boolean")) &&
950
- (value.metadata === undefined || value.metadata && typeof value.metadata === "object" && !Array.isArray(value.metadata) &&
92
+ (value.metadata === undefined || isRecord(value.metadata) &&
951
93
  (value.metadata.count === undefined || Number.isSafeInteger(value.metadata.count) && value.metadata.count === value.sites.length) &&
952
- (value.metadata.cursors === undefined || value.metadata.cursors && typeof value.metadata.cursors === "object" && !Array.isArray(value.metadata.cursors) &&
94
+ (value.metadata.cursors === undefined || isRecord(value.metadata.cursors) &&
953
95
  ["next", "prev"].every((key) => value.metadata.cursors[key] === undefined || cursor(value.metadata.cursors[key]))))
954
96
  }
955
97
  }
@@ -962,10 +104,10 @@ function wixSiteOperation(operation) {
962
104
  let siteId = settings.siteId, values = input;
963
105
  if (input && Object.hasOwn(input, "siteId")) {
964
106
  ({ siteId, ...values } = input);
965
- if (!guid(siteId)) throw new ConnectorError("connector_input_invalid", "Choose a valid Wix Site ID.", { statusCode: 422, messageCode: "connectors.wix.chooseValidSiteId" });
107
+ if (!isUuid(siteId)) throw new ConnectorError("connector_input_invalid", "Choose a valid Wix Site ID.", { statusCode: 422, messageCode: "connectors.wix.chooseValidSiteId" });
966
108
  const allowed = (settings.selectableSiteIds || "").split(",").map(id => id.trim().toLowerCase());
967
109
  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" });
110
+ } else if (!isUuid(siteId)) throw new ConnectorError("connector_input_invalid", "Configure the Wix Site ID first.", { statusCode: 422, messageCode: "connectors.wix.configureSiteIdFirst" });
969
111
  const request = operation.request(values, { ...settings, siteId });
970
112
  return { ...request, headers: { ...request.headers, "wix-site-id": siteId } };
971
113
  }