@jskit-ai/connectors-catalog 0.1.67 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1722 -0
- package/README.md +11 -0
- package/locales/en.json +3139 -0
- package/package.json +7 -6
- package/src/server/ai.js +4 -4
- package/src/server/amazon-redshift.js +5 -5
- package/src/server/apify.js +1 -1
- package/src/server/apollo-io.js +1 -1
- package/src/server/asana.js +3 -3
- package/src/server/ashby.js +3 -3
- package/src/server/attention.js +2 -2
- package/src/server/aws-athena.js +3 -3
- package/src/server/aws-s3.js +1 -1
- package/src/server/awsTransport.js +9 -9
- package/src/server/brevo.js +3 -3
- package/src/server/calendly.js +2 -2
- package/src/server/chargebee.js +1 -1
- package/src/server/databricks.js +2 -2
- package/src/server/dbt-semantic-layer.js +8 -8
- package/src/server/elevenlabs.js +4 -4
- package/src/server/firebase-cloud-messaging.js +8 -8
- package/src/server/firecrawl.js +2 -2
- package/src/server/gatewayapi.js +4 -4
- package/src/server/github-api.js +3 -3
- package/src/server/gitlab-api.js +1 -1
- package/src/server/gmail.js +1 -1
- package/src/server/gong.js +4 -4
- package/src/server/google-ads-search.js +16 -16
- package/src/server/google-ads.js +7 -7
- package/src/server/google-drive.js +8 -6
- package/src/server/google-maps-platform.js +5 -5
- package/src/server/google-search-console.js +1 -1
- package/src/server/google-sheets.js +3 -2
- package/src/server/google.js +3 -2
- package/src/server/granola.js +5 -5
- package/src/server/hubspot.js +5 -3
- package/src/server/inngest.js +4 -4
- package/src/server/klipy.js +1 -1
- package/src/server/lightspeed.js +4 -4
- package/src/server/linear.js +2 -2
- package/src/server/linkedin.js +6 -6
- package/src/server/mapbox.js +1 -1
- package/src/server/mcpRegistration.js +3 -2
- package/src/server/mcpTransport.js +1 -1
- package/src/server/microsoft-fabric.js +3 -3
- package/src/server/microsoft-onedrive.js +1 -1
- package/src/server/microsoft-onenote.js +1 -1
- package/src/server/microsoft-outlook.js +1 -1
- package/src/server/microsoft.js +4 -3
- package/src/server/n8n.js +4 -4
- package/src/server/oura.js +1 -1
- package/src/server/paddle.js +1 -1
- package/src/server/pipedrive.js +4 -4
- package/src/server/posthog.js +3 -3
- package/src/server/salesforce.js +8 -8
- package/src/server/semrush.js +25 -25
- package/src/server/shopify.js +6 -6
- package/src/server/slack.js +10 -10
- package/src/server/snowflake.js +5 -5
- package/src/server/tally.js +1 -1
- package/src/server/telegram.js +1 -1
- package/src/server/tiktok.js +14 -14
- package/src/server/twilio.js +3 -3
- package/src/server/twitch.js +22 -21
- package/src/server/wave.js +34 -34
- package/src/server/wix.js +73 -73
- package/src/server/wiz.js +18 -18
- package/src/server/woocommerce.js +23 -23
- package/src/server/wordpress-com.js +3 -3
- package/src/server/wordpress-self-hosted.js +2 -2
- package/src/server/workday.js +3 -3
- package/src/server/x-twitter.js +6 -6
- package/src/server/xero.js +15 -15
- package/src/server/zoho-books.js +8 -8
- package/src/server/zoho-crm.js +5 -5
- package/src/server/zohoOAuth.js +1 -1
- package/src/shared/ai.js +24 -13
- package/src/shared/algolia.js +4 -4
- package/src/shared/amazon-redshift.js +16 -16
- package/src/shared/amplitude.js +6 -6
- package/src/shared/atlassian.js +19 -19
- package/src/shared/aws.js +18 -18
- package/src/shared/canva.js +18 -18
- package/src/shared/chargebee.js +3 -3
- package/src/shared/clickhouse.js +9 -9
- package/src/shared/confidence.js +6 -6
- package/src/shared/databricks.js +8 -8
- package/src/shared/dbt-semantic-layer.js +7 -7
- package/src/shared/english-ai.js +6 -0
- package/src/shared/english-definitions.js +34 -0
- package/src/shared/english-texts.js +7 -0
- package/src/shared/figma.js +2 -2
- package/src/shared/firebase-cloud-messaging.js +11 -11
- package/src/shared/firecrawl.js +1 -1
- package/src/shared/gemini-enterprise.js +7 -7
- package/src/shared/gong.js +5 -5
- package/src/shared/google-ads.js +5 -5
- package/src/shared/google-maps-platform.js +7 -7
- package/src/shared/google.js +40 -40
- package/src/shared/granola.js +5 -5
- package/src/shared/hex.js +7 -7
- package/src/shared/inngest.js +8 -8
- package/src/shared/lightspeed.js +52 -52
- package/src/shared/linkedin.js +6 -6
- package/src/shared/logo-dev.js +3 -3
- package/src/shared/mapbox.js +6 -6
- package/src/shared/mcp.js +9 -9
- package/src/shared/microsoft-fabric.js +9 -9
- package/src/shared/microsoft.js +52 -52
- package/src/shared/miro.js +5 -5
- package/src/shared/oura.js +5 -5
- package/src/shared/posthog.js +9 -9
- package/src/shared/prestashop.js +5 -5
- package/src/shared/resend.js +1 -1
- package/src/shared/salesforce.js +8 -8
- package/src/shared/semrush.js +6 -6
- package/src/shared/shopify.js +26 -26
- package/src/shared/slack.js +61 -61
- package/src/shared/snowflake.js +15 -15
- package/src/shared/tiktok.js +7 -7
- package/src/shared/tokens.js +126 -126
- package/src/shared/twilio.js +7 -7
- package/src/shared/twitch.js +24 -24
- package/src/shared/wave.js +33 -33
- package/src/shared/wix.js +5 -5
- package/src/shared/wiz.js +14 -14
- package/src/shared/wordpress-com.js +9 -9
- package/src/shared/wordpress.js +11 -11
- package/src/shared/workday.js +8 -8
- package/src/shared/x-twitter.js +5 -5
- package/src/shared/xero.js +25 -25
- package/src/shared/zoho-books.js +10 -10
- package/src/shared/zoho-crm.js +16 -16
- package/src/shared/zoho-regions.js +8 -8
- package/test/englishDefinitions.test.js +37 -0
- package/test/i18nCatalog.test.js +16 -0
- package/test/shopify.test.js +3 -1
package/src/server/twitch.js
CHANGED
|
@@ -55,7 +55,7 @@ function analyticsOperation(kind, identity) {
|
|
|
55
55
|
const query = new URL(request.url).searchParams;
|
|
56
56
|
const start = query.get("started_at"), end = query.get("ended_at");
|
|
57
57
|
if (Boolean(start) !== Boolean(end) || (start && start > end)) {
|
|
58
|
-
throw new ConnectorError("connector_input_invalid", "Supply both report dates, with the end on or after the start.", { statusCode: 422 });
|
|
58
|
+
throw new ConnectorError("connector_input_invalid", "Supply both report dates, with the end on or after the start.", { statusCode: 422, messageCode: "connectors.twitch.supplyBothReportDatesEnd" });
|
|
59
59
|
}
|
|
60
60
|
return request;
|
|
61
61
|
} };
|
|
@@ -105,7 +105,7 @@ function scheduleWrite(fields, method) {
|
|
|
105
105
|
const request = operation.request(input);
|
|
106
106
|
const url = new URL(request.url);
|
|
107
107
|
if (request.body.id) { url.searchParams.set("id", request.body.id); delete request.body.id; }
|
|
108
|
-
if (method === "PATCH" && !Object.keys(request.body).length) throw new ConnectorError("connector_input_invalid", "Supply at least one schedule change.", { statusCode: 422 });
|
|
108
|
+
if (method === "PATCH" && !Object.keys(request.body).length) throw new ConnectorError("connector_input_invalid", "Supply at least one schedule change.", { statusCode: 422, messageCode: "connectors.twitch.supplyLeastScheduleChange" });
|
|
109
109
|
if (request.body.duration !== undefined) request.body.duration = String(request.body.duration);
|
|
110
110
|
return { ...request, url: url.href, body: method === "DELETE" ? undefined : request.body };
|
|
111
111
|
} };
|
|
@@ -118,36 +118,37 @@ function rewardWrite(path, fields, method, queryNames, validateResult) {
|
|
|
118
118
|
for (const [enabled, amount] of [["is_max_per_stream_enabled", "max_per_stream"],
|
|
119
119
|
["is_max_per_user_per_stream_enabled", "max_per_user_per_stream"], ["is_global_cooldown_enabled", "global_cooldown_seconds"]]) {
|
|
120
120
|
if (request.body[enabled] === true && request.body[amount] === undefined) {
|
|
121
|
-
throw new ConnectorError("connector_input_invalid", `Supply ${amount} when enabling this reward limit.`,
|
|
121
|
+
throw new ConnectorError("connector_input_invalid", `Supply ${amount} when enabling this reward limit.`,
|
|
122
|
+
{ statusCode: 422, messageCode: "connectors.twitch.rewardLimitAmountRequired", messageParams: { field: amount } });
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
for (const name of queryNames) { url.searchParams.set(name, request.body[name]); delete request.body[name]; }
|
|
125
|
-
if (method === "PATCH" && Object.keys(request.body).length === 0) throw new ConnectorError("connector_input_invalid", "Supply at least one reward change.", { statusCode: 422 });
|
|
126
|
+
if (method === "PATCH" && Object.keys(request.body).length === 0) throw new ConnectorError("connector_input_invalid", "Supply at least one reward change.", { statusCode: 422, messageCode: "connectors.twitch.supplyLeastRewardChange" });
|
|
126
127
|
return { ...request, url: url.href, body: method === "DELETE" ? undefined : request.body };
|
|
127
128
|
} };
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
async function normalizeTokenResponse(response) {
|
|
131
132
|
const failure = {
|
|
132
|
-
401: ["connector_reconnect_required", "Connect this Twitch account again."],
|
|
133
|
-
403: ["connector_permission_denied", "Twitch denied the token request."],
|
|
134
|
-
429: ["connector_rate_limited", "Twitch's request limit was reached."]
|
|
133
|
+
401: ["connector_reconnect_required", "Connect this Twitch account again.", { messageCode: "connectors.shared.reconnectAccount", messageParams: { provider: "Twitch" } }],
|
|
134
|
+
403: ["connector_permission_denied", "Twitch denied the token request.", { messageCode: "connectors.twitch.tokenRequestDenied" }],
|
|
135
|
+
429: ["connector_rate_limited", "Twitch's request limit was reached.", { messageCode: "connectors.shared.requestLimitReached", messageParams: { provider: "Twitch" } }]
|
|
135
136
|
}[response.status];
|
|
136
|
-
if (failure) throw new ConnectorError(
|
|
137
|
+
if (failure) throw new ConnectorError(failure[0], failure[1], { statusCode: response.status, ...failure[2] });
|
|
137
138
|
let value;
|
|
138
139
|
try { value = await response.clone().json(); } catch {
|
|
139
140
|
if (!response.ok) return response;
|
|
140
|
-
throw new ConnectorError("connector_response_invalid", "Twitch returned an invalid token response.", { statusCode: 502 });
|
|
141
|
+
throw new ConnectorError("connector_response_invalid", "Twitch returned an invalid token response.", { statusCode: 502, messageCode: "connectors.shared.invalidTokenResponse", messageParams: { provider: "Twitch" } });
|
|
141
142
|
}
|
|
142
143
|
if (!response.ok) {
|
|
143
144
|
if (response.status === 400 && value?.message === "Invalid refresh token") {
|
|
144
|
-
throw new ConnectorError("connector_reconnect_required", "Connect this Twitch account again.", { statusCode: 401 });
|
|
145
|
+
throw new ConnectorError("connector_reconnect_required", "Connect this Twitch account again.", { statusCode: 401, messageCode: "connectors.shared.reconnectAccount", messageParams: { provider: "Twitch" } });
|
|
145
146
|
}
|
|
146
147
|
return response;
|
|
147
148
|
}
|
|
148
149
|
if (typeof value?.scope === "string" && (value.scope === "" || /^[^\s]+(?: [^\s]+)*$/u.test(value.scope))) return response;
|
|
149
150
|
if (!scopeList(value?.scope)) {
|
|
150
|
-
throw new ConnectorError("connector_response_invalid", "Twitch returned an invalid permission grant.", { statusCode: 502 });
|
|
151
|
+
throw new ConnectorError("connector_response_invalid", "Twitch returned an invalid permission grant.", { statusCode: 502, messageCode: "connectors.shared.invalidPermissionGrant", messageParams: { provider: "Twitch" } });
|
|
151
152
|
}
|
|
152
153
|
return Response.json({ ...value, scope: value.scope.join(" ") }, { status: response.status });
|
|
153
154
|
}
|
|
@@ -200,24 +201,24 @@ const twitchProvider = Object.freeze({
|
|
|
200
201
|
const operation = `${options.method} ${url.pathname}`;
|
|
201
202
|
if (!(url.href === validationUrl && options.method === "GET") &&
|
|
202
203
|
(url.origin !== "https://api.twitch.tv" || !Object.hasOwn(permissions, operation))) {
|
|
203
|
-
throw new ConnectorError("connector_destination_invalid", "This Twitch operation has an invalid destination.");
|
|
204
|
+
throw new ConnectorError("connector_destination_invalid", "This Twitch operation has an invalid destination.", { messageCode: "connectors.twitch.operationInvalidDestination" });
|
|
204
205
|
}
|
|
205
206
|
const validation = await request(validationUrl, { ...options, method: "GET", body: undefined });
|
|
206
207
|
if (!validToken(validation)) {
|
|
207
|
-
throw new ConnectorError("connector_response_invalid", "Twitch returned an invalid user-token validation.", { statusCode: 502 });
|
|
208
|
+
throw new ConnectorError("connector_response_invalid", "Twitch returned an invalid user-token validation.", { statusCode: 502, messageCode: "connectors.twitch.returnedInvalidUserTokenValidation" });
|
|
208
209
|
}
|
|
209
210
|
if (validation.client_id !== new Headers(options.headers).get("Client-Id")) {
|
|
210
|
-
throw new ConnectorError("connector_reconnect_required", "The Twitch token belongs to a different application.", { statusCode: 401 });
|
|
211
|
+
throw new ConnectorError("connector_reconnect_required", "The Twitch token belongs to a different application.", { statusCode: 401, messageCode: "connectors.twitch.tokenBelongsDifferentApplication" });
|
|
211
212
|
}
|
|
212
213
|
if (url.href === validationUrl) return validation;
|
|
213
214
|
const requiredScope = permissions[operation];
|
|
214
215
|
if (requiredScope && ![requiredScope].flat().some(scope => validation.scopes.includes(scope))) {
|
|
215
|
-
throw new ConnectorError("connector_reconnect_required", "Connect Twitch again with the permission needed for this operation.", { statusCode: 401 });
|
|
216
|
+
throw new ConnectorError("connector_reconnect_required", "Connect Twitch again with the permission needed for this operation.", { statusCode: 401, messageCode: "connectors.twitch.connectAgainPermissionNeededOperation" });
|
|
216
217
|
}
|
|
217
218
|
if (operation === "POST /helix/eventsub/subscriptions") {
|
|
218
219
|
const [version, scopes] = eventTypes[options.body.type];
|
|
219
220
|
if (scopes.length && !scopes.some(scope => validation.scopes.includes(scope))) {
|
|
220
|
-
throw new ConnectorError("connector_reconnect_required", "Connect Twitch again with permission for this event.", { statusCode: 401 });
|
|
221
|
+
throw new ConnectorError("connector_reconnect_required", "Connect Twitch again with permission for this event.", { statusCode: 401, messageCode: "connectors.twitch.connectAgainPermissionEvent" });
|
|
221
222
|
}
|
|
222
223
|
options = { ...options, body: { type: options.body.type, version,
|
|
223
224
|
condition: { broadcaster_user_id: validation.user_id },
|
|
@@ -238,7 +239,7 @@ const twitchProvider = Object.freeze({
|
|
|
238
239
|
if (url.pathname === "/helix/channels/followers" && !url.searchParams.has("broadcaster_id")) url.searchParams.set("broadcaster_id", validation.user_id);
|
|
239
240
|
const result = await request(url.href, options);
|
|
240
241
|
if (url.pathname === "/helix/users" && result?.data?.[0]?.id !== validation.user_id) {
|
|
241
|
-
throw new ConnectorError("connector_response_invalid", "Twitch returned a profile for a different user.", { statusCode: 502 });
|
|
242
|
+
throw new ConnectorError("connector_response_invalid", "Twitch returned a profile for a different user.", { statusCode: 502, messageCode: "connectors.twitch.returnedProfileDifferentUser" });
|
|
242
243
|
}
|
|
243
244
|
return result;
|
|
244
245
|
},
|
|
@@ -258,7 +259,7 @@ const twitchProvider = Object.freeze({
|
|
|
258
259
|
const query = new URL(request.url).searchParams;
|
|
259
260
|
if (query.get("is_vacation_enabled") === "true" && (!query.has("timezone") || !query.has("vacation_start_time") || !query.has("vacation_end_time") ||
|
|
260
261
|
Date.parse(query.get("vacation_end_time")) <= Date.parse(query.get("vacation_start_time")))) {
|
|
261
|
-
throw new ConnectorError("connector_input_invalid", "Enabling vacation requires a timezone and an end after the start.", { statusCode: 422 });
|
|
262
|
+
throw new ConnectorError("connector_input_invalid", "Enabling vacation requires a timezone and an end after the start.", { statusCode: 422, messageCode: "connectors.twitch.enablingVacationRequiresTimezoneEnd" });
|
|
262
263
|
}
|
|
263
264
|
return { ...request, method: "PATCH" };
|
|
264
265
|
} },
|
|
@@ -325,7 +326,7 @@ const twitchProvider = Object.freeze({
|
|
|
325
326
|
"predictions.end": helixOperation("predictions", { id: opaqueId,
|
|
326
327
|
status: { type: "string", required: true, enum: ["LOCKED", "CANCELED", "RESOLVED"] }, winning_outcome_id: { ...opaqueId, required: false }
|
|
327
328
|
}, ["channel:manage:predictions"], interactionResult, "PATCH", values => {
|
|
328
|
-
if ((values.status === "RESOLVED") !== Boolean(values.winning_outcome_id)) throw new ConnectorError("connector_input_invalid", "Supply a winning outcome only when resolving the prediction.", { statusCode: 422 });
|
|
329
|
+
if ((values.status === "RESOLVED") !== Boolean(values.winning_outcome_id)) throw new ConnectorError("connector_input_invalid", "Supply a winning outcome only when resolving the prediction.", { statusCode: 422, messageCode: "connectors.twitch.supplyWinningOutcomeResolvingPrediction" });
|
|
329
330
|
}),
|
|
330
331
|
"chat.send": helixOperation("chat/messages", { broadcaster_id: { ...idField, required: true },
|
|
331
332
|
message: { type: "string", required: true, noTrim: true, minLength: 1, maxLength: 500 }, reply_parent_message_id: { ...opaqueId, required: false }
|
|
@@ -346,7 +347,7 @@ const twitchProvider = Object.freeze({
|
|
|
346
347
|
scopes: ["channel:manage:broadcast"], validateResult: broadcastUpdate.validateResult,
|
|
347
348
|
request(input) {
|
|
348
349
|
const request = broadcastUpdate.request(input);
|
|
349
|
-
if (!Object.keys(request.body).length) throw new ConnectorError("connector_input_invalid", "Supply at least one broadcast change.", { statusCode: 422 });
|
|
350
|
+
if (!Object.keys(request.body).length) throw new ConnectorError("connector_input_invalid", "Supply at least one broadcast change.", { statusCode: 422, messageCode: "connectors.twitch.supplyLeastBroadcastChange" });
|
|
350
351
|
return { ...request, method: "PATCH" };
|
|
351
352
|
}
|
|
352
353
|
},
|
|
@@ -371,7 +372,7 @@ const twitchProvider = Object.freeze({
|
|
|
371
372
|
});
|
|
372
373
|
|
|
373
374
|
function parseTwitchEventSubMessage(raw, { broadcasterId, sessionId, subscriptionIds = [] } = {}) {
|
|
374
|
-
const invalid = () => new ConnectorError("connector_event_invalid", "Twitch returned an invalid or unexpected EventSub message.", { statusCode: 422 });
|
|
375
|
+
const invalid = () => new ConnectorError("connector_event_invalid", "Twitch returned an invalid or unexpected EventSub message.", { statusCode: 422, messageCode: "connectors.twitch.returnedInvalidUnexpectedEventsubMessage" });
|
|
375
376
|
if (typeof raw !== "string" || Buffer.byteLength(raw, "utf8") > 1048576 || !userId(broadcasterId)) throw invalid();
|
|
376
377
|
let message;
|
|
377
378
|
try { message = JSON.parse(raw); } catch { throw invalid(); }
|
package/src/server/wave.js
CHANGED
|
@@ -164,13 +164,13 @@ const waveProvider = Object.freeze({
|
|
|
164
164
|
apiOrigins: ["https://gql.waveapps.com"], checkOperation: "user.read",
|
|
165
165
|
async normalizeTokenResponse(response) {
|
|
166
166
|
if (response.status === 403) {
|
|
167
|
-
throw new ConnectorError("connector_permission_denied", "Wave denied token access. Check the business subscription and permissions.", { statusCode: 403 });
|
|
167
|
+
throw new ConnectorError("connector_permission_denied", "Wave denied token access. Check the business subscription and permissions.", { statusCode: 403, messageCode: "connectors.wave.deniedTokenAccessCheckBusiness" });
|
|
168
168
|
}
|
|
169
169
|
if (response.ok) {
|
|
170
170
|
let value;
|
|
171
171
|
try { value = await response.clone().json(); } catch { /* reported below */ }
|
|
172
172
|
if (typeof value?.scope !== "string" || !/^[^\s]+(?: [^\s]+)*$/u.test(value.scope)) {
|
|
173
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an invalid permission grant.", { statusCode: 502 });
|
|
173
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an invalid permission grant.", { statusCode: 502, messageCode: "connectors.shared.invalidPermissionGrant", messageParams: { provider: "Wave" } });
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
return response;
|
|
@@ -311,9 +311,9 @@ const waveProvider = Object.freeze({
|
|
|
311
311
|
const result = await transport(url, init);
|
|
312
312
|
if (Array.isArray(result?.errors) && result.errors.length) {
|
|
313
313
|
const codes = result.errors.map((error) => error?.extensions?.code);
|
|
314
|
-
if (codes.includes("UNAUTHENTICATED")) throw new ConnectorError("connector_reconnect_required", "Connect this Wave account again.", { statusCode: 401 });
|
|
315
|
-
if (codes.includes("NOT_FOUND")) throw new ConnectorError("connector_resource_not_found", "This Wave resource is not available.", { statusCode: 404 });
|
|
316
|
-
throw new ConnectorError("connector_provider_failed", "Wave could not complete this query.", { statusCode: 502 });
|
|
314
|
+
if (codes.includes("UNAUTHENTICATED")) throw new ConnectorError("connector_reconnect_required", "Connect this Wave account again.", { statusCode: 401, messageCode: "connectors.shared.reconnectAccount", messageParams: { provider: "Wave" } });
|
|
315
|
+
if (codes.includes("NOT_FOUND")) throw new ConnectorError("connector_resource_not_found", "This Wave resource is not available.", { statusCode: 404, messageCode: "connectors.shared.resourceUnavailable", messageParams: { provider: "Wave" } });
|
|
316
|
+
throw new ConnectorError("connector_provider_failed", "Wave could not complete this query.", { statusCode: 502, messageCode: "connectors.wave.couldNotCompleteQuery" });
|
|
317
317
|
}
|
|
318
318
|
return result;
|
|
319
319
|
};
|
|
@@ -333,7 +333,7 @@ const waveProvider = Object.freeze({
|
|
|
333
333
|
if (line.customerId) related.set(`customer:${line.customerId}`, { field: "customer", id: line.customerId });
|
|
334
334
|
for (const tax of line.taxes || []) related.set(`salesTax:${tax.salesTaxId}`, { field: "salesTax", id: tax.salesTaxId });
|
|
335
335
|
}
|
|
336
|
-
if (related.size > 100) throw new ConnectorError("connector_input_invalid", "Use at most 100 distinct related records per transaction.", { statusCode: 422 });
|
|
336
|
+
if (related.size > 100) throw new ConnectorError("connector_input_invalid", "Use at most 100 distinct related records per transaction.", { statusCode: 422, messageCode: "connectors.wave.useMost100DistinctRelated" });
|
|
337
337
|
for (const target of related.values()) {
|
|
338
338
|
const owner = await request(address, { ...options, body: {
|
|
339
339
|
query: `query ConnectorWaveTransactionOwner($businessId: ID!, $id: ID!) { business(id: $businessId) { id record: ${target.field}(id: $id) { id business { id } } } }`,
|
|
@@ -341,7 +341,7 @@ const waveProvider = Object.freeze({
|
|
|
341
341
|
} });
|
|
342
342
|
if (owner?.data?.business?.id !== variables.businessId || owner.data.business.record?.id !== target.id ||
|
|
343
343
|
owner.data.business.record.business?.id !== variables.businessId) {
|
|
344
|
-
throw new ConnectorError("connector_resource_not_found", "A transaction account, customer or tax is not available in the selected business. Check read access.", { statusCode: 404 });
|
|
344
|
+
throw new ConnectorError("connector_resource_not_found", "A transaction account, customer or tax is not available in the selected business. Check read access.", { statusCode: 404, messageCode: "connectors.wave.transactionAccountCustomerTaxNot" });
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
347
|
options = { ...options, body: { ...options.body, variables: { input: { ...values, businessId: variables.businessId } } } };
|
|
@@ -352,17 +352,17 @@ const waveProvider = Object.freeze({
|
|
|
352
352
|
const values = variables.input;
|
|
353
353
|
if (accountingMatch[2] !== "Create") {
|
|
354
354
|
const changedFields = Object.keys(values).filter(key => key !== "id" && key !== "sequence");
|
|
355
|
-
if (accountingMatch[2] === "Patch" && !changedFields.length) throw new ConnectorError("connector_input_invalid", "Choose accounting fields to update.", { statusCode: 422 });
|
|
355
|
+
if (accountingMatch[2] === "Patch" && !changedFields.length) throw new ConnectorError("connector_input_invalid", "Choose accounting fields to update.", { statusCode: 422, messageCode: "connectors.wave.chooseAccountingFieldsUpdate" });
|
|
356
356
|
const existing = await request(address, { ...options, body: {
|
|
357
357
|
query: `query ConnectorWaveAccountingOwner($businessId: ID!, $id: ID!) { business(id: $businessId) { id record: ${accountingResource}(id: $id) { id business { id } ${accountingResource === "account" ? "sequence" : ""} } } }`,
|
|
358
358
|
variables: { businessId: variables.businessId, id: values.id }
|
|
359
359
|
} });
|
|
360
360
|
if (existing?.data?.business?.id !== variables.businessId || existing.data.business.record?.id !== values.id ||
|
|
361
361
|
existing.data.business.record.business?.id !== variables.businessId) {
|
|
362
|
-
throw new ConnectorError("connector_resource_not_found", "This accounting record is not available in the selected business. Check read access.", { statusCode: 404 });
|
|
362
|
+
throw new ConnectorError("connector_resource_not_found", "This accounting record is not available in the selected business. Check read access.", { statusCode: 404, messageCode: "connectors.wave.accountingRecordNotAvailableSelected" });
|
|
363
363
|
}
|
|
364
364
|
if (accountingMatch[2] === "Patch" && accountingResource === "account" && existing.data.business.record.sequence !== values.sequence) {
|
|
365
|
-
throw new ConnectorError("connector_conflict", "The account changed. Reload it before applying your edit.", { statusCode: 409 });
|
|
365
|
+
throw new ConnectorError("connector_conflict", "The account changed. Reload it before applying your edit.", { statusCode: 409, messageCode: "connectors.wave.accountChangedReloadApplyingEdit" });
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
368
|
options = { ...options, body: { ...options.body, variables: { input: accountingMatch[2] === "Create" ? { ...values, businessId: variables.businessId } : values } } };
|
|
@@ -371,7 +371,7 @@ const waveProvider = Object.freeze({
|
|
|
371
371
|
if (productAction) {
|
|
372
372
|
const values = variables.input;
|
|
373
373
|
if (productAction === "Patch" && Object.keys(values).length < 2) {
|
|
374
|
-
throw new ConnectorError("connector_input_invalid", "Choose at least one product field to update.", { statusCode: 422 });
|
|
374
|
+
throw new ConnectorError("connector_input_invalid", "Choose at least one product field to update.", { statusCode: 422, messageCode: "connectors.wave.chooseLeastProductFieldUpdate" });
|
|
375
375
|
}
|
|
376
376
|
const related = [];
|
|
377
377
|
if (productAction === "Patch") related.push({ field: "product", id: values.id });
|
|
@@ -386,7 +386,7 @@ const waveProvider = Object.freeze({
|
|
|
386
386
|
} });
|
|
387
387
|
if (owner?.data?.business?.id !== variables.businessId ||
|
|
388
388
|
owner.data.business.record?.id !== target.id || owner.data.business.record.business?.id !== variables.businessId) {
|
|
389
|
-
throw new ConnectorError("connector_resource_not_found", "This product, account or tax is not available in the selected business. Check read access.", { statusCode: 404 });
|
|
389
|
+
throw new ConnectorError("connector_resource_not_found", "This product, account or tax is not available in the selected business. Check read access.", { statusCode: 404, messageCode: "connectors.wave.productAccountTaxNotAvailable" });
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
options = { ...options, body: { ...options.body, variables: { input: productAction === "Create" ? { ...values, businessId: variables.businessId } : values } } };
|
|
@@ -394,7 +394,7 @@ const waveProvider = Object.freeze({
|
|
|
394
394
|
if (documentAction) {
|
|
395
395
|
const values = variables.input;
|
|
396
396
|
if (documentAction === "Patch" && Object.keys(values).length < 2) {
|
|
397
|
-
throw new ConnectorError("connector_input_invalid", "Choose at least one document field to update.", { statusCode: 422 });
|
|
397
|
+
throw new ConnectorError("connector_input_invalid", "Choose at least one document field to update.", { statusCode: 422, messageCode: "connectors.wave.chooseLeastDocumentFieldUpdate" });
|
|
398
398
|
}
|
|
399
399
|
const targetId = values.id || values[`${resource}Id`];
|
|
400
400
|
if (targetId) {
|
|
@@ -404,13 +404,13 @@ const waveProvider = Object.freeze({
|
|
|
404
404
|
} });
|
|
405
405
|
if (existing?.data?.business?.id !== variables.businessId ||
|
|
406
406
|
existing.data.business[resource]?.id !== targetId || (resource === "invoice" ? existing.data.business[resource].business?.id : existing.data.business[resource].customer?.business?.id) !== variables.businessId) {
|
|
407
|
-
throw new ConnectorError("connector_resource_not_found", "This document is not available in the selected business. Check read access.", { statusCode: 404 });
|
|
407
|
+
throw new ConnectorError("connector_resource_not_found", "This document is not available in the selected business. Check read access.", { statusCode: 404, messageCode: "connectors.wave.documentNotAvailableSelectedBusiness" });
|
|
408
408
|
}
|
|
409
409
|
const status = existing.data.business[resource].status;
|
|
410
410
|
if (documentAction === "Send" && ((resource === "invoice" && existing.data.business.emailSendEnabled !== true) ||
|
|
411
411
|
typeof status !== "string" || !status || status === "DRAFT" ||
|
|
412
412
|
(resource === "estimate" && !["APPROVED", "SENT", "VIEWED", "ACCEPTED"].includes(status)))) {
|
|
413
|
-
throw new ConnectorError("connector_input_invalid", "Approve the document and check Wave email eligibility before sending.", { statusCode: 422 });
|
|
413
|
+
throw new ConnectorError("connector_input_invalid", "Approve the document and check Wave email eligibility before sending.", { statusCode: 422, messageCode: "connectors.wave.approveDocumentCheckEmailEligibility" });
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
416
|
if (values.customerId) {
|
|
@@ -420,14 +420,14 @@ const waveProvider = Object.freeze({
|
|
|
420
420
|
} });
|
|
421
421
|
if (owner?.data?.business?.id !== variables.businessId || owner.data.business.customer?.id !== values.customerId ||
|
|
422
422
|
owner.data.business.customer.business?.id !== variables.businessId) {
|
|
423
|
-
throw new ConnectorError("connector_resource_not_found", "This customer is not available in the selected business. Check read access.", { statusCode: 404 });
|
|
423
|
+
throw new ConnectorError("connector_resource_not_found", "This customer is not available in the selected business. Check read access.", { statusCode: 404, messageCode: "connectors.wave.customerNotAvailableCheckAccess" });
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
options = { ...options, body: { ...options.body, variables: { input: documentAction === "Create" ? { ...values, businessId: variables.businessId } : values } } };
|
|
427
427
|
}
|
|
428
428
|
if (patchCustomer) {
|
|
429
429
|
if (Object.keys(variables.input).length < 2) {
|
|
430
|
-
throw new ConnectorError("connector_input_invalid", "Choose at least one customer field to update.", { statusCode: 422 });
|
|
430
|
+
throw new ConnectorError("connector_input_invalid", "Choose at least one customer field to update.", { statusCode: 422, messageCode: "connectors.wave.chooseLeastCustomerFieldUpdate" });
|
|
431
431
|
}
|
|
432
432
|
const existing = await request(address, { ...options, body: {
|
|
433
433
|
query: "query ConnectorWaveCustomerOwner($businessId: ID!, $id: ID!) { business(id: $businessId) { id customer(id: $id) { id business { id } } } }",
|
|
@@ -435,7 +435,7 @@ const waveProvider = Object.freeze({
|
|
|
435
435
|
} });
|
|
436
436
|
if (existing?.data?.business?.id !== variables.businessId || existing.data.business.customer?.id !== variables.input.id ||
|
|
437
437
|
existing.data.business.customer.business?.id !== variables.businessId) {
|
|
438
|
-
throw new ConnectorError("connector_resource_not_found", "This customer is not available in the selected business.", { statusCode: 404 });
|
|
438
|
+
throw new ConnectorError("connector_resource_not_found", "This customer is not available in the selected business.", { statusCode: 404, messageCode: "connectors.wave.customerNotAvailable" });
|
|
439
439
|
}
|
|
440
440
|
// Business selection is a local authorization input; CustomerPatchInput only takes its customer ID.
|
|
441
441
|
options = { ...options, body: { ...options.body, variables: { input: variables.input } } };
|
|
@@ -443,76 +443,76 @@ const waveProvider = Object.freeze({
|
|
|
443
443
|
const result = await request(address, options);
|
|
444
444
|
if (transactionCreate) {
|
|
445
445
|
if (result?.data?.moneyTransactionCreate?.didSucceed === false) {
|
|
446
|
-
throw new ConnectorError("connector_input_invalid", "Wave rejected the transaction. Review its balance, accounts and details before submitting again.", { statusCode: 422 });
|
|
446
|
+
throw new ConnectorError("connector_input_invalid", "Wave rejected the transaction. Review its balance, accounts and details before submitting again.", { statusCode: 422, messageCode: "connectors.wave.rejectedTransactionReviewBalanceAccounts" });
|
|
447
447
|
}
|
|
448
448
|
return result;
|
|
449
449
|
}
|
|
450
450
|
if (accountingMatch) {
|
|
451
451
|
const outcome = result?.data?.[`${accountingResource}${accountingMatch[2]}`];
|
|
452
|
-
if (outcome?.didSucceed === false) throw new ConnectorError("connector_input_invalid", "Wave rejected the accounting change. Check the values and current record.", { statusCode: 422 });
|
|
452
|
+
if (outcome?.didSucceed === false) throw new ConnectorError("connector_input_invalid", "Wave rejected the accounting change. Check the values and current record.", { statusCode: 422, messageCode: "connectors.wave.rejectedAccountingChangeCheckValues" });
|
|
453
453
|
if (outcome?.didSucceed === true && accountingMatch[2] !== "Archive" && (outcome[accountingResource]?.business?.id !== variables.businessId ||
|
|
454
454
|
(variables.input.id && outcome[accountingResource]?.id !== variables.input.id))) {
|
|
455
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected accounting record. Check the outcome before retrying.", { statusCode: 502 });
|
|
455
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected accounting record. Check the outcome before retrying.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedAccountingRecordCheck" });
|
|
456
456
|
}
|
|
457
457
|
return result;
|
|
458
458
|
}
|
|
459
459
|
if (productAction) {
|
|
460
460
|
const value = result?.data?.[`product${productAction}`];
|
|
461
461
|
if (value?.didSucceed === false) {
|
|
462
|
-
throw new ConnectorError("connector_input_invalid", "Wave rejected the product details. Check accounts, taxes and price.", { statusCode: 422 });
|
|
462
|
+
throw new ConnectorError("connector_input_invalid", "Wave rejected the product details. Check accounts, taxes and price.", { statusCode: 422, messageCode: "connectors.wave.rejectedProductDetailsCheckAccounts" });
|
|
463
463
|
}
|
|
464
464
|
if (value?.didSucceed === true && (value.product?.business?.id !== variables.businessId ||
|
|
465
465
|
(productAction === "Patch" && value.product?.id !== variables.input.id))) {
|
|
466
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected product. Check the outcome before retrying.", { statusCode: 502 });
|
|
466
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected product. Check the outcome before retrying.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedProductCheckOutcome" });
|
|
467
467
|
}
|
|
468
468
|
return result;
|
|
469
469
|
}
|
|
470
470
|
if (documentAction) {
|
|
471
471
|
const outcome = result?.data?.[`${resource}${documentAction}`];
|
|
472
472
|
if (outcome?.didSucceed === false) {
|
|
473
|
-
throw new ConnectorError("connector_input_invalid", "Wave rejected the document operation. Check its details and current state.", { statusCode: 422 });
|
|
473
|
+
throw new ConnectorError("connector_input_invalid", "Wave rejected the document operation. Check its details and current state.", { statusCode: 422, messageCode: "connectors.wave.rejectedDocumentOperationCheckDetails" });
|
|
474
474
|
}
|
|
475
475
|
if (outcome?.didSucceed === true && documentAction !== "Send" &&
|
|
476
476
|
((resource === "invoice" ? outcome[resource]?.business?.id : outcome[resource]?.customer?.business?.id) !== variables.businessId ||
|
|
477
477
|
((variables.input.id || variables.input[`${resource}Id`]) && outcome[resource]?.id !== (variables.input.id || variables.input[`${resource}Id`])) ||
|
|
478
478
|
(variables.input.customerId && outcome[resource]?.customer?.id !== variables.input.customerId))) {
|
|
479
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected document. Check the outcome before retrying.", { statusCode: 502 });
|
|
479
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected document. Check the outcome before retrying.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedDocumentCheckOutcome" });
|
|
480
480
|
}
|
|
481
481
|
return result;
|
|
482
482
|
}
|
|
483
483
|
const mutation = patchCustomer ? result?.data?.customerPatch : result?.data?.customerCreate;
|
|
484
484
|
if (mutation?.didSucceed === false) {
|
|
485
|
-
throw new ConnectorError("connector_input_invalid", "Wave rejected the customer details. Check the selected business and customer fields.", { statusCode: 422 });
|
|
485
|
+
throw new ConnectorError("connector_input_invalid", "Wave rejected the customer details. Check the selected business and customer fields.", { statusCode: 422, messageCode: "connectors.wave.rejectedCustomerDetailsCheckSelected" });
|
|
486
486
|
}
|
|
487
487
|
if (mutation?.didSucceed === true && mutation.customer?.business?.id !== (patchCustomer ? variables.businessId : variables.input?.businessId)) {
|
|
488
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned a customer from an unexpected business. Check the outcome before retrying.", { statusCode: 502 });
|
|
488
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned a customer from an unexpected business. Check the outcome before retrying.", { statusCode: 502, messageCode: "connectors.wave.returnedCustomerUnexpectedBusinessCheck" });
|
|
489
489
|
}
|
|
490
490
|
if (!patchCustomer && variables.businessId && result?.data?.business?.id !== variables.businessId) {
|
|
491
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected business.", { statusCode: 502 });
|
|
491
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected business.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedBusiness" });
|
|
492
492
|
}
|
|
493
493
|
if (patchCustomer && mutation?.customer?.id !== variables.input.id) {
|
|
494
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected customer. Check the outcome before retrying.", { statusCode: 502 });
|
|
494
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected customer. Check the outcome before retrying.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedCustomerCheckOutcome" });
|
|
495
495
|
}
|
|
496
496
|
if (variables.invoiceId && (result?.data?.business?.invoice?.id !== variables.invoiceId ||
|
|
497
497
|
result?.data?.business?.invoice?.business?.id !== variables.businessId)) {
|
|
498
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected invoice.", { statusCode: 502 });
|
|
498
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected invoice.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedInvoice" });
|
|
499
499
|
}
|
|
500
500
|
if (variables.estimateId && (result?.data?.business?.estimate?.id !== variables.estimateId ||
|
|
501
501
|
result?.data?.business?.estimate?.customer?.business?.id !== variables.businessId)) {
|
|
502
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected estimate.", { statusCode: 502 });
|
|
502
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected estimate.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedEstimate" });
|
|
503
503
|
}
|
|
504
504
|
if (result?.data?.business?.estimates?.edges?.some(edge => edge?.node?.customer?.business?.id !== variables.businessId)) {
|
|
505
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an estimate from another business.", { statusCode: 502 });
|
|
505
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an estimate from another business.", { statusCode: 502, messageCode: "connectors.wave.returnedEstimateAnotherBusiness" });
|
|
506
506
|
}
|
|
507
507
|
for (const field of ["account", "salesTax"]) {
|
|
508
508
|
if (variables[`${field}Id`] && (result?.data?.business?.[field]?.id !== variables[`${field}Id`] ||
|
|
509
509
|
result?.data?.business?.[field]?.business?.id !== variables.businessId)) {
|
|
510
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected accounting record.", { statusCode: 502 });
|
|
510
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected accounting record.", { statusCode: 502, messageCode: "connectors.wave.returnedUnexpectedAccountingRecord" });
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
513
|
const page = result?.data?.businesses || Object.values(result?.data?.business || {}).find(value => value && typeof value === "object" && Object.hasOwn(value, "pageInfo"));
|
|
514
514
|
if (page && (page.pageInfo?.currentPage !== variables.page || page.edges?.length > variables.pageSize)) {
|
|
515
|
-
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected page.", { statusCode: 502 });
|
|
515
|
+
throw new ConnectorError("connector_response_invalid", "Wave returned an unexpected page.", { statusCode: 502, messageCode: "connectors.shared.unexpectedPage", messageParams: { provider: "Wave" } });
|
|
516
516
|
}
|
|
517
517
|
return result;
|
|
518
518
|
}
|