@fargowealth/fargo-ai-cli 1.3.0 → 1.4.1
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 +22 -0
- package/README.md +17 -0
- package/node_modules/@fargowealth/website-connector-runtime/README.md +15 -0
- package/node_modules/@fargowealth/website-connector-runtime/package.json +24 -0
- package/node_modules/@fargowealth/website-connector-runtime/src/index.d.mts +136 -0
- package/node_modules/@fargowealth/website-connector-runtime/src/index.mjs +825 -0
- package/package.json +5 -1
- package/src/constants.mjs +1 -1
- package/src/website-browser-profile.mjs +50 -7
- package/src/website-cli.mjs +81 -6
- package/src/website-connector-definition.d.mts +1 -58
- package/src/website-connector-definition.mjs +1 -339
|
@@ -1,339 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const OPERATION_ID = /^[a-z][a-z0-9_]{1,62}[a-z0-9]$/;
|
|
3
|
-
const TEMPLATE_ARGUMENT = /\{\{([a-zA-Z][a-zA-Z0-9_]*)\}\}/g;
|
|
4
|
-
const FORBIDDEN_HEADERS = new Set([
|
|
5
|
-
"connection",
|
|
6
|
-
"content-length",
|
|
7
|
-
"cookie",
|
|
8
|
-
"host",
|
|
9
|
-
"proxy-authorization",
|
|
10
|
-
"transfer-encoding",
|
|
11
|
-
"upgrade"
|
|
12
|
-
]);
|
|
13
|
-
|
|
14
|
-
export const WEBSITE_CONNECTOR_SCHEMA_VERSION = "fargo.website-connector/v1";
|
|
15
|
-
|
|
16
|
-
export class WebsiteConnectorDefinitionError extends Error {
|
|
17
|
-
constructor(code, path = "") {
|
|
18
|
-
super(path ? `${code}:${path}` : code);
|
|
19
|
-
this.name = "WebsiteConnectorDefinitionError";
|
|
20
|
-
this.code = code;
|
|
21
|
-
this.path = path;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function parseWebsiteConnectorDefinition(value) {
|
|
26
|
-
const source = record(value, "website_connector_definition_invalid", "/");
|
|
27
|
-
equal(source.schemaVersion, WEBSITE_CONNECTOR_SCHEMA_VERSION, "website_connector_schema_version_invalid", "/schemaVersion");
|
|
28
|
-
const connectorId = requiredText(source.connectorId, 64, "website_connector_id_invalid", "/connectorId");
|
|
29
|
-
if (!CONNECTOR_ID.test(connectorId)) fail("website_connector_id_invalid", "/connectorId");
|
|
30
|
-
const displayName = requiredText(source.displayName, 120, "website_connector_display_name_invalid", "/displayName");
|
|
31
|
-
const allowedOrigins = uniqueArray(source.allowedOrigins, 1, 12, "/allowedOrigins").map((origin, index) => (
|
|
32
|
-
exactHttpsOrigin(origin, "website_connector_origin_invalid", `/allowedOrigins/${index}`)
|
|
33
|
-
));
|
|
34
|
-
const loginUrl = optionalHttpsUrl(source.loginUrl, allowedOrigins, "/loginUrl");
|
|
35
|
-
const auth = parseAuthRecipe(source.auth, allowedOrigins);
|
|
36
|
-
if (auth.kind === "browser_storage" && !loginUrl) fail("website_connector_login_url_required", "/loginUrl");
|
|
37
|
-
const operations = uniqueArray(source.operations, 1, 80, "/operations").map((operation, index) => (
|
|
38
|
-
parseOperation(operation, allowedOrigins, index)
|
|
39
|
-
));
|
|
40
|
-
const operationIds = new Set();
|
|
41
|
-
for (const operation of operations) {
|
|
42
|
-
if (operationIds.has(operation.operationId)) fail("website_operation_id_duplicate", `/operations/${operation.operationId}`);
|
|
43
|
-
if (operation.requiresConnection && auth.kind !== "browser_storage") {
|
|
44
|
-
fail("website_operation_connection_auth_required", `/operations/${operation.operationId}/requiresConnection`);
|
|
45
|
-
}
|
|
46
|
-
operationIds.add(operation.operationId);
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
schemaVersion: WEBSITE_CONNECTOR_SCHEMA_VERSION,
|
|
50
|
-
connectorId,
|
|
51
|
-
displayName,
|
|
52
|
-
allowedOrigins,
|
|
53
|
-
...(loginUrl ? { loginUrl } : {}),
|
|
54
|
-
auth,
|
|
55
|
-
operations
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function connectorToolName(connectorId, operationId) {
|
|
60
|
-
const left = String(connectorId || "").replaceAll("-", "_");
|
|
61
|
-
return `${left}_${operationId}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function renderWebsiteOperation(definition, operationId, rawArguments) {
|
|
65
|
-
const parsed = parseWebsiteConnectorDefinition(definition);
|
|
66
|
-
const operation = parsed.operations.find((candidate) => candidate.operationId === operationId);
|
|
67
|
-
if (!operation) fail("website_operation_not_found", "/operationId");
|
|
68
|
-
const args = record(rawArguments ?? {}, "website_operation_arguments_invalid", "/arguments");
|
|
69
|
-
validateArguments(args, operation.inputSchema);
|
|
70
|
-
const path = renderString(operation.request.path, args, "/request/path");
|
|
71
|
-
const url = new URL(path, `${operation.request.origin}/`);
|
|
72
|
-
if (url.origin !== operation.request.origin) fail("website_operation_origin_escape", "/request/path");
|
|
73
|
-
for (const [name, argumentName] of Object.entries(operation.request.query)) {
|
|
74
|
-
const value = args[argumentName];
|
|
75
|
-
if (value !== undefined && value !== null) url.searchParams.set(name, scalarText(value, `/arguments/${argumentName}`));
|
|
76
|
-
}
|
|
77
|
-
const body = operation.request.body === undefined
|
|
78
|
-
? undefined
|
|
79
|
-
: renderBody(operation.request.body, args, "/request/body");
|
|
80
|
-
return {
|
|
81
|
-
definition: parsed,
|
|
82
|
-
operation,
|
|
83
|
-
request: {
|
|
84
|
-
body,
|
|
85
|
-
headers: { ...operation.request.headers },
|
|
86
|
-
method: operation.request.method,
|
|
87
|
-
url: url.toString()
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function parseAuthRecipe(value, allowedOrigins) {
|
|
93
|
-
const source = value === undefined ? { kind: "none" } : record(value, "website_auth_recipe_invalid", "/auth");
|
|
94
|
-
if (source.kind === "none") return { kind: "none" };
|
|
95
|
-
equal(source.kind, "browser_storage", "website_auth_recipe_invalid", "/auth/kind");
|
|
96
|
-
const sendCookies = source.sendCookies === true;
|
|
97
|
-
const headerSources = source.headerSources === undefined
|
|
98
|
-
? []
|
|
99
|
-
: uniqueArray(source.headerSources, 0, 12, "/auth/headerSources").map((item, index) => {
|
|
100
|
-
const binding = record(item, "website_auth_header_source_invalid", `/auth/headerSources/${index}`);
|
|
101
|
-
const name = headerName(binding.name, `/auth/headerSources/${index}/name`, true);
|
|
102
|
-
const origin = exactHttpsOrigin(binding.origin, "website_auth_header_source_invalid", `/auth/headerSources/${index}/origin`);
|
|
103
|
-
if (!allowedOrigins.includes(origin)) fail("website_auth_origin_not_allowed", `/auth/headerSources/${index}/origin`);
|
|
104
|
-
const keySuffix = requiredText(binding.keySuffix, 240, "website_auth_header_source_invalid", `/auth/headerSources/${index}/keySuffix`);
|
|
105
|
-
const prefix = optionalHeaderPrefix(binding.prefix, `/auth/headerSources/${index}/prefix`);
|
|
106
|
-
return { keySuffix, name, origin, ...(prefix ? { prefix } : {}) };
|
|
107
|
-
});
|
|
108
|
-
if (!sendCookies && headerSources.length === 0) fail("website_auth_recipe_empty", "/auth");
|
|
109
|
-
return { kind: "browser_storage", sendCookies, headerSources };
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function parseOperation(value, allowedOrigins, index) {
|
|
113
|
-
const path = `/operations/${index}`;
|
|
114
|
-
const source = record(value, "website_operation_invalid", path);
|
|
115
|
-
const operationId = requiredText(source.operationId, 64, "website_operation_id_invalid", `${path}/operationId`);
|
|
116
|
-
if (!OPERATION_ID.test(operationId)) fail("website_operation_id_invalid", `${path}/operationId`);
|
|
117
|
-
const description = requiredText(source.description, 500, "website_operation_description_invalid", `${path}/description`);
|
|
118
|
-
const readOnly = source.readOnly === true;
|
|
119
|
-
const requiresConnection = source.requiresConnection === true;
|
|
120
|
-
const riskLevel = optionalEnum(source.riskLevel, ["low", "medium", "high", "critical"], readOnly ? "low" : "medium", `${path}/riskLevel`);
|
|
121
|
-
const inputSchema = parseInputSchema(source.inputSchema, `${path}/inputSchema`);
|
|
122
|
-
const requestSource = record(source.request, "website_operation_request_invalid", `${path}/request`);
|
|
123
|
-
const origin = exactHttpsOrigin(requestSource.origin, "website_operation_origin_invalid", `${path}/request/origin`);
|
|
124
|
-
if (!allowedOrigins.includes(origin)) fail("website_operation_origin_not_allowed", `${path}/request/origin`);
|
|
125
|
-
const method = optionalEnum(requestSource.method, ["GET", "POST", "PUT", "PATCH", "DELETE"], "GET", `${path}/request/method`);
|
|
126
|
-
if (readOnly && method !== "GET") fail("website_operation_read_only_method_invalid", `${path}/request/method`);
|
|
127
|
-
const requestPath = requiredText(requestSource.path, 2_000, "website_operation_path_invalid", `${path}/request/path`);
|
|
128
|
-
if (!requestPath.startsWith("/") || requestPath.startsWith("//") || requestPath.includes("\\")) {
|
|
129
|
-
fail("website_operation_path_invalid", `${path}/request/path`);
|
|
130
|
-
}
|
|
131
|
-
const pathArguments = [...requestPath.matchAll(TEMPLATE_ARGUMENT)].map((match) => match[1]);
|
|
132
|
-
const properties = record(inputSchema.properties ?? {}, "website_operation_input_schema_invalid", `${path}/inputSchema/properties`);
|
|
133
|
-
for (const argument of pathArguments) {
|
|
134
|
-
if (!Object.hasOwn(properties, argument)) fail("website_operation_template_argument_unknown", `${path}/request/path`);
|
|
135
|
-
}
|
|
136
|
-
const query = requestSource.query === undefined
|
|
137
|
-
? {}
|
|
138
|
-
: Object.fromEntries(Object.entries(record(requestSource.query, "website_operation_query_invalid", `${path}/request/query`)).map(([name, argument]) => {
|
|
139
|
-
if (!/^[A-Za-z0-9_.~-]{1,120}$/.test(name)) fail("website_operation_query_name_invalid", `${path}/request/query/${name}`);
|
|
140
|
-
const argumentName = requiredText(argument, 120, "website_operation_query_argument_invalid", `${path}/request/query/${name}`);
|
|
141
|
-
if (!Object.hasOwn(properties, argumentName)) fail("website_operation_query_argument_unknown", `${path}/request/query/${name}`);
|
|
142
|
-
return [name, argumentName];
|
|
143
|
-
}));
|
|
144
|
-
const headers = requestSource.headers === undefined
|
|
145
|
-
? {}
|
|
146
|
-
: Object.fromEntries(Object.entries(record(requestSource.headers, "website_operation_headers_invalid", `${path}/request/headers`)).map(([name, headerValue]) => {
|
|
147
|
-
const normalized = requiredText(headerValue, 500, "website_operation_header_value_invalid", `${path}/request/headers/${name}`);
|
|
148
|
-
if (/[\r\n\0]/.test(normalized)) fail("website_operation_header_value_invalid", `${path}/request/headers/${name}`);
|
|
149
|
-
return [headerName(name, `${path}/request/headers/${name}`, false), normalized];
|
|
150
|
-
}));
|
|
151
|
-
const body = requestSource.body;
|
|
152
|
-
if (body !== undefined && method === "GET") fail("website_operation_get_body_invalid", `${path}/request/body`);
|
|
153
|
-
if (body !== undefined) validateBodyTemplate(body, properties, `${path}/request/body`);
|
|
154
|
-
return {
|
|
155
|
-
operationId,
|
|
156
|
-
description,
|
|
157
|
-
readOnly,
|
|
158
|
-
requiresConnection,
|
|
159
|
-
riskLevel,
|
|
160
|
-
inputSchema,
|
|
161
|
-
request: {
|
|
162
|
-
origin,
|
|
163
|
-
method,
|
|
164
|
-
path: requestPath,
|
|
165
|
-
query,
|
|
166
|
-
headers,
|
|
167
|
-
...(body === undefined ? {} : { body })
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function optionalHeaderPrefix(value, path) {
|
|
173
|
-
if (value === undefined || value === null || value === "") return "";
|
|
174
|
-
if (typeof value !== "string" || value.length > 80 || /[\r\n\0]/.test(value)) {
|
|
175
|
-
fail("website_auth_header_source_invalid", path);
|
|
176
|
-
}
|
|
177
|
-
return value;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function parseInputSchema(value, path) {
|
|
181
|
-
const source = value === undefined
|
|
182
|
-
? { additionalProperties: false, properties: {}, type: "object" }
|
|
183
|
-
: record(value, "website_operation_input_schema_invalid", path);
|
|
184
|
-
equal(source.type, "object", "website_operation_input_schema_invalid", `${path}/type`);
|
|
185
|
-
if (source.additionalProperties !== false) fail("website_operation_input_schema_open", `${path}/additionalProperties`);
|
|
186
|
-
const properties = record(source.properties ?? {}, "website_operation_input_schema_invalid", `${path}/properties`);
|
|
187
|
-
const normalizedProperties = {};
|
|
188
|
-
for (const [name, candidate] of Object.entries(properties)) {
|
|
189
|
-
if (!/^[A-Za-z][A-Za-z0-9_]{0,119}$/.test(name)) fail("website_operation_input_name_invalid", `${path}/properties/${name}`);
|
|
190
|
-
const property = record(candidate, "website_operation_input_property_invalid", `${path}/properties/${name}`);
|
|
191
|
-
const type = optionalEnum(property.type, ["string", "number", "integer", "boolean"], "string", `${path}/properties/${name}/type`);
|
|
192
|
-
normalizedProperties[name] = {
|
|
193
|
-
type,
|
|
194
|
-
...(typeof property.maxLength === "number" ? { maxLength: boundedInteger(property.maxLength, 1, 20_000, `${path}/properties/${name}/maxLength`) } : {}),
|
|
195
|
-
...(Array.isArray(property.enum) ? { enum: property.enum.map((item) => scalar(item, `${path}/properties/${name}/enum`)) } : {})
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
const required = source.required === undefined ? [] : uniqueArray(source.required, 0, Object.keys(normalizedProperties).length, `${path}/required`).map(String);
|
|
199
|
-
for (const name of required) if (!Object.hasOwn(normalizedProperties, name)) fail("website_operation_required_unknown", `${path}/required`);
|
|
200
|
-
return { additionalProperties: false, properties: normalizedProperties, required, type: "object" };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function validateArguments(args, schema) {
|
|
204
|
-
for (const required of schema.required) if (!Object.hasOwn(args, required)) fail("website_operation_argument_required", `/arguments/${required}`);
|
|
205
|
-
for (const [name, value] of Object.entries(args)) {
|
|
206
|
-
const property = schema.properties[name];
|
|
207
|
-
if (!property) fail("website_operation_argument_unknown", `/arguments/${name}`);
|
|
208
|
-
if (property.type === "string" && typeof value !== "string") fail("website_operation_argument_type_invalid", `/arguments/${name}`);
|
|
209
|
-
if (property.type === "number" && (typeof value !== "number" || !Number.isFinite(value))) fail("website_operation_argument_type_invalid", `/arguments/${name}`);
|
|
210
|
-
if (property.type === "integer" && !Number.isInteger(value)) fail("website_operation_argument_type_invalid", `/arguments/${name}`);
|
|
211
|
-
if (property.type === "boolean" && typeof value !== "boolean") fail("website_operation_argument_type_invalid", `/arguments/${name}`);
|
|
212
|
-
if (property.maxLength && typeof value === "string" && value.length > property.maxLength) fail("website_operation_argument_too_long", `/arguments/${name}`);
|
|
213
|
-
if (property.enum && !property.enum.some((candidate) => Object.is(candidate, value))) fail("website_operation_argument_enum_invalid", `/arguments/${name}`);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function validateBodyTemplate(value, properties, path) {
|
|
218
|
-
if (typeof value === "string") {
|
|
219
|
-
if (value.startsWith("$") && !Object.hasOwn(properties, value.slice(1))) fail("website_operation_body_argument_unknown", path);
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
if (value === null || typeof value === "number" || typeof value === "boolean") return;
|
|
223
|
-
if (Array.isArray(value)) {
|
|
224
|
-
value.forEach((item, index) => validateBodyTemplate(item, properties, `${path}/${index}`));
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const source = record(value, "website_operation_body_invalid", path);
|
|
228
|
-
for (const [name, item] of Object.entries(source)) validateBodyTemplate(item, properties, `${path}/${name}`);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function renderBody(value, args, path) {
|
|
232
|
-
if (typeof value === "string") {
|
|
233
|
-
if (value.startsWith("$")) return args[value.slice(1)];
|
|
234
|
-
return renderString(value, args, path);
|
|
235
|
-
}
|
|
236
|
-
if (Array.isArray(value)) return value.map((item, index) => renderBody(item, args, `${path}/${index}`));
|
|
237
|
-
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, renderBody(item, args, `${path}/${name}`)]));
|
|
238
|
-
return value;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function renderString(value, args, path) {
|
|
242
|
-
return String(value).replace(TEMPLATE_ARGUMENT, (_match, name) => encodeURIComponent(scalarText(args[name], `/arguments/${name || path}`)));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function scalarText(value, path) {
|
|
246
|
-
scalar(value, path);
|
|
247
|
-
return String(value);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function scalar(value, path) {
|
|
251
|
-
if (!["string", "number", "boolean"].includes(typeof value) || (typeof value === "number" && !Number.isFinite(value))) {
|
|
252
|
-
fail("website_operation_argument_scalar_required", path);
|
|
253
|
-
}
|
|
254
|
-
return value;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function headerName(value, path, allowAuthorization) {
|
|
258
|
-
const name = requiredText(value, 100, "website_operation_header_name_invalid", path);
|
|
259
|
-
const normalized = name.toLowerCase();
|
|
260
|
-
if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(name) || FORBIDDEN_HEADERS.has(normalized) || (!allowAuthorization && normalized === "authorization")) {
|
|
261
|
-
fail("website_operation_header_name_invalid", path);
|
|
262
|
-
}
|
|
263
|
-
return name;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function exactHttpsOrigin(value, code, path) {
|
|
267
|
-
const text = requiredText(value, 500, code, path);
|
|
268
|
-
try {
|
|
269
|
-
const url = new URL(text);
|
|
270
|
-
if (url.protocol !== "https:" || url.origin !== text || url.username || url.password || !publicHostname(url.hostname)) fail(code, path);
|
|
271
|
-
return url.origin;
|
|
272
|
-
} catch (error) {
|
|
273
|
-
if (error instanceof WebsiteConnectorDefinitionError) throw error;
|
|
274
|
-
fail(code, path);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function publicHostname(value) {
|
|
279
|
-
const hostname = String(value).toLowerCase().replace(/^\[|\]$/g, "");
|
|
280
|
-
if (!hostname.includes(".") || /^[0-9.]+$/.test(hostname) || hostname.includes(":")) return false;
|
|
281
|
-
return !hostname.endsWith(".localhost")
|
|
282
|
-
&& !hostname.endsWith(".local")
|
|
283
|
-
&& !hostname.endsWith(".internal")
|
|
284
|
-
&& hostname !== "metadata.google.internal";
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function optionalHttpsUrl(value, allowedOrigins, path) {
|
|
288
|
-
if (value === undefined || value === null || value === "") return undefined;
|
|
289
|
-
try {
|
|
290
|
-
const url = new URL(requiredText(value, 2_000, "website_connector_login_url_invalid", path));
|
|
291
|
-
if (url.protocol !== "https:" || !allowedOrigins.includes(url.origin) || url.username || url.password || url.hash) fail("website_connector_login_url_invalid", path);
|
|
292
|
-
return url.toString();
|
|
293
|
-
} catch (error) {
|
|
294
|
-
if (error instanceof WebsiteConnectorDefinitionError) throw error;
|
|
295
|
-
fail("website_connector_login_url_invalid", path);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function record(value, code, path) {
|
|
300
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) fail(code, path);
|
|
301
|
-
return value;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function uniqueArray(value, min, max, path) {
|
|
305
|
-
if (!Array.isArray(value) || value.length < min || value.length > max) fail("website_connector_array_invalid", path);
|
|
306
|
-
const serialized = value.map((item) => typeof item === "string" ? item : JSON.stringify(item));
|
|
307
|
-
if (new Set(serialized).size !== serialized.length) fail("website_connector_array_duplicate", path);
|
|
308
|
-
return value;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function requiredText(value, max, code, path) {
|
|
312
|
-
const text = typeof value === "string" ? value.trim() : "";
|
|
313
|
-
if (!text || text.length > max) fail(code, path);
|
|
314
|
-
return text;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function optionalText(value, max, code, path) {
|
|
318
|
-
if (value === undefined || value === null || value === "") return undefined;
|
|
319
|
-
return requiredText(value, max, code, path);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function optionalEnum(value, allowed, fallback, path) {
|
|
323
|
-
const candidate = value === undefined ? fallback : value;
|
|
324
|
-
if (!allowed.includes(candidate)) fail("website_connector_enum_invalid", path);
|
|
325
|
-
return candidate;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function boundedInteger(value, min, max, path) {
|
|
329
|
-
if (!Number.isInteger(value) || value < min || value > max) fail("website_connector_integer_invalid", path);
|
|
330
|
-
return value;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function equal(actual, expected, code, path) {
|
|
334
|
-
if (actual !== expected) fail(code, path);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function fail(code, path) {
|
|
338
|
-
throw new WebsiteConnectorDefinitionError(code, path);
|
|
339
|
-
}
|
|
1
|
+
export * from "@fargowealth/website-connector-runtime";
|