@linode/validation
Version:
Yup validation schemas for use with the Linode APIv4
1,291 lines (1,278 loc) • 101 kB
JavaScript
// src/account.schema.ts
import { array as array2, boolean as boolean2, mixed, number as number2, object as object2, string as string2 } from "yup";
// src/profile.schema.ts
import { isPossiblePhoneNumber } from "libphonenumber-js";
import { array, boolean, number, object, string } from "yup";
var EMAIL_VALIDATION_REGEX = new RegExp(
// eslint-disable-next-line sonarjs/regex-complexity
/^(?!-.*|.*(\.{2}|@-))[a-zA-Z0-9_.+"-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]*[a-zA-Z0-9]$/
);
var createPersonalAccessTokenSchema = object({
scopes: string(),
expiry: string(),
label: string().min(1, "Label must be between 1 and 100 characters.").max(100, "Label must be between 1 and 100 characters.")
});
var createSSHKeySchema = object({
label: string().required("Label is required.").min(1, "Label must be between 1 and 64 characters.").max(64, "Label must be between 1 and 64 characters.").trim(),
ssh_key: string()
});
var updateSSHKeySchema = object({
label: string().required("Label is required.").min(1, "Label must be between 1 and 64 characters.").max(64, "Label must be between 1 and 64 characters.").trim()
});
var emailSchema = string().required("Email address is required.").max(128, "Email address must be 128 characters or less.").email("Must be a valid email address.").matches(EMAIL_VALIDATION_REGEX, `Invalid email address.`);
var updateProfileSchema = object({
email: string().email().matches(EMAIL_VALIDATION_REGEX, `Invalid email address. `),
timezone: string(),
email_notifications: boolean(),
authorized_keys: array().of(string()),
restricted: boolean(),
two_factor_auth: boolean(),
lish_auth_method: string().oneOf(["password_keys", "keys_only", "disabled"]),
authentication_type: string().oneOf(["password", "github"])
});
var SendCodeToPhoneNumberSchema = object({
iso_code: string().required(),
phone_number: string().test(
"is-phone-number",
"Not a valid phone number",
(phone_number, context) => {
const { iso_code } = context.parent;
if (!phone_number) {
return false;
}
return isPossiblePhoneNumber(phone_number, iso_code);
}
)
});
var VerifyPhoneNumberCodeSchema = object({
otp_code: string().required("Verification Code is required.").test(
"digits only",
"The verification code must only contain digits.",
(value) => {
if (!value) {
return true;
}
return /^\d+$/.test(value);
}
)
});
var SecurityQuestionsSchema = object({
security_questions: array().of(
object({
question_id: number().required("You must pick a question."),
response: string().min(3, "Answers must be at least 3 characters.").max(17, "Answers must be at most 17 characters.").required("You must provide an answer to each security question.")
}).required()
).length(3, "You must answer all 3 security questions.").required()
});
// src/account.schema.ts
var updateAccountSchema = object2({
email: string2().max(128, "Email must be 128 characters or less."),
address_1: string2().max(64, "Address must be 64 characters or less."),
city: string2().max(24, "City must be 24 characters or less."),
company: string2().max(128, "Company must be 128 characters or less."),
country: string2().min(2, "Country code must be two letters.").max(2, "Country code must be two letters."),
first_name: string2().max(50, "First name must be 50 characters or less."),
last_name: string2().max(50, "Last name must be 50 characters or less."),
address_2: string2().max(64, "Address must be 64 characters or less."),
phone: string2().max(32, "Phone number must be 32 characters or less."),
state: string2().max(24, "State must be 24 characters or less."),
tax_id: string2().max(100, "Tax ID must be 100 characters or less."),
zip: string2().max(16, "Zip code must be 16 characters or less.")
});
var createOAuthClientSchema = object2({
label: string2().required("Label is required.").min(1, "Label must be between 1 and 512 characters.").max(512, "Label must be between 1 and 512 characters."),
redirect_uri: string2().required("Redirect URI is required.")
});
var updateOAuthClientSchema = object2({
label: string2().min(1, "Label must be between 1 and 512 characters.").max(512, "Label must be between 1 and 512 characters."),
redirect_uri: string2()
});
var PaymentSchema = object2({
usd: string2().required("USD payment amount is required.")
});
var CreditCardSchema = object2({
card_number: string2().required("Credit card number is required.").min(13, "Credit card number must be between 13 and 23 characters.").max(23, "Credit card number must be between 13 and 23 characters."),
expiry_year: number2().test(
"length",
"Expiration year must be 2 or 4 digits.",
(value) => [2, 4].includes(String(value).length)
).required("Expiration year is required.").typeError("Expiration year must be a number.").min((/* @__PURE__ */ new Date()).getFullYear(), "Expiration year must not be in the past.").max((/* @__PURE__ */ new Date()).getFullYear() + 20, "Expiry too far in the future."),
expiry_month: number2().required("Expiration month is required.").typeError("Expiration month must be a number.").min(1, "Expiration month must be a number from 1 to 12.").max(12, "Expiration month must be a number from 1 to 12."),
cvv: string2().required("Security code is required.").min(3, "Security code must be between 3 and 4 characters.").max(4, "Security code must be between 3 and 4 characters.")
});
var PaymentMethodSchema = object2({
type: mixed().oneOf(
["credit_card", "payment_method_nonce"],
"Type must be credit_card or payment_method_nonce."
),
data: object2().when("type", {
is: "credit_card",
then: () => CreditCardSchema,
otherwise: () => object2({
nonce: string2().required("Payment nonce is required.")
})
}),
is_default: boolean2().required(
"You must indicate if this should be your default method of payment."
)
});
var userNameErrors = {
lengthError: "Username must be between 3 and 32 characters.",
consecutiveError: "Username must not include two dashes or underscores in a row.",
charsError: "Username may only contain letters, numbers, dashes, and underscores and must begin and end with letters or numbers.",
spacesError: "Username may not contain spaces or tabs.",
nonAsciiError: "Username must only use ASCII characters."
};
var usernameSchema = string2().required("Username is required.").min(3, userNameErrors.lengthError).max(32, userNameErrors.lengthError).test("ascii-only", userNameErrors.nonAsciiError, (value) => {
if (!value) return false;
return [...value].every((char) => char.charCodeAt(0) <= 127);
}).test("no-whitespace", userNameErrors.spacesError, (value) => {
if (!value) return true;
return !/[ \t]/.test(value);
}).test(
"no-consecutive-separators",
userNameErrors.consecutiveError,
(value) => {
if (!value) return true;
return !value.includes("__") && !value.includes("--");
}
).test("valid-characters", userNameErrors.charsError, (value) => {
if (!value) return false;
const firstChar = value[0];
const lastChar = value[value.length - 1];
const isAlphaNum = /[a-zA-Z0-9]/;
if (!isAlphaNum.test(firstChar) || !isAlphaNum.test(lastChar)) {
return false;
}
return /^[a-zA-Z0-9_-]+$/.test(value);
});
var CreateUserSchema = object2({
username: usernameSchema,
email: emailSchema,
restricted: boolean2().required(
"You must indicate if this user should have restricted access."
)
});
var UpdateUserNameSchema = object2({
username: usernameSchema
});
var UpdateUserEmailSchema = object2({
email: emailSchema
});
var UpdateUserSchema = object2({
username: string2().min(3, userNameErrors.lengthError).max(32, userNameErrors.lengthError),
email: string2().email("Must be a valid email address."),
restricted: boolean2()
});
var GrantSchema = object2({
id: number2().required("ID is required."),
permissions: string2().oneOf(
["read_only", "read_write"],
"Permissions must be null, read_only, or read_write."
).nullable("Permissions must be null, read_only, or read_write.")
});
var UpdateGrantSchema = object2({
global: object2(),
linode: array2().of(GrantSchema),
domain: array2().of(GrantSchema),
nodebalancer: array2().of(GrantSchema),
image: array2().of(GrantSchema),
longview: array2().of(GrantSchema),
stackscript: array2().of(GrantSchema),
volume: array2().of(GrantSchema)
});
var UpdateAccountSettingsSchema = object2({
network_helper: boolean2(),
backups_enabled: boolean2(),
managed: boolean2(),
longview_subscription: string2().nullable(),
object_storage: string2(),
interfaces_for_new_linodes: string2()
});
var PromoCodeSchema = object2({
promo_code: string2().required("Promo code is required.").min(1, "Promo code must be between 1 and 32 characters.").max(32, "Promo code must be between 1 and 32 characters.")
});
// src/buckets.schema.ts
import { boolean as boolean3, object as object3, string as string3 } from "yup";
var ENDPOINT_TYPES = ["E0", "E1", "E2", "E3"];
var CreateBucketSchema = object3().shape(
{
label: string3().required("Bucket name is required.").min(3, "Bucket name must be between 3 and 63 characters.").matches(/^\S*$/, "Bucket name must not contain spaces.").matches(
/^[a-z0-9].*[a-z0-9]$/,
"Bucket name must start and end with a lowercase letter or number."
).matches(
/^(?!.*[.-]{2})[a-z0-9.-]+$/,
"Bucket name must contain only lowercase letters, numbers, periods (.), and hyphens (-). Adjacent periods and hyphens are not allowed."
).max(63, "Bucket name must be between 3 and 63 characters.").test(
"unique-label",
"A bucket with this name already exists in your selected region",
(value, context) => {
var _a;
const { cluster, region } = context.parent;
const buckets = (_a = context.options.context) == null ? void 0 : _a.buckets;
if (!Array.isArray(buckets)) {
return true;
}
return !buckets.some(
(bucket) => bucket.label === value && (bucket.cluster === cluster || bucket.region === region)
);
}
),
cluster: string3().when("region", {
is: (region) => !region || region.length === 0,
then: (schema) => schema.required("Cluster is required.")
}),
region: string3().when("cluster", {
is: (cluster) => !cluster || cluster.length === 0,
then: (schema) => schema.required("Region is required.")
}),
endpoint_type: string3().oneOf([...ENDPOINT_TYPES]).optional(),
cors_enabled: boolean3().optional(),
acl: string3().oneOf([
"private",
"public-read",
"authenticated-read",
"public-read-write"
]).optional(),
s3_endpoint: string3().optional()
},
[["cluster", "region"]]
).test("cors-enabled-check", "Invalid CORS configuration.", function(value) {
const { endpoint_type, cors_enabled } = value;
if ((endpoint_type === "E0" || endpoint_type === "E1") && !cors_enabled) {
return this.createError({
path: "cors_enabled",
message: "CORS must be enabled for endpoint type E0 or E1."
});
}
if ((endpoint_type === "E2" || endpoint_type === "E3") && cors_enabled) {
return this.createError({
path: "cors_enabled",
message: "CORS must be disabled for endpoint type E2 or E3."
});
}
return true;
});
var UploadCertificateSchema = object3({
certificate: string3().required("Certificate is required."),
private_key: string3().required("Private key is required.")
});
var UpdateBucketAccessSchema = object3({
acl: string3().oneOf([
"private",
"public-read",
"authenticated-read",
"public-read-write"
]).notRequired(),
cors_enabled: boolean3().notRequired()
});
// src/cloudnats.schema.ts
import { array as array3, number as number3, object as object4, string as string4 } from "yup";
var VALID_PORT_SIZES = [
64,
128,
256,
512,
1024,
2048,
4096,
8192,
16384
];
var createCloudNATSchema = object4({
addresses: array3().of(
object4({
address: string4().required("Address must be a string.")
})
).notRequired(),
label: string4().required("Label is required.").max(150, "Label must be 150 characters or fewer."),
port_prefix_default_len: number3().oneOf(VALID_PORT_SIZES, "Invalid port size.").notRequired(),
region: string4().required("Region is required.")
});
var updateCloudNATSchema = object4({
label: string4().max(150, "Label must be 150 characters or fewer.")
});
// src/cloudpulse.schema.ts
import { array as array4, number as number4, object as object5, string as string5 } from "yup";
var fieldErrorMessage = "This field is required.";
var dimensionFilters = object5({
dimension_label: string5().required(fieldErrorMessage),
operator: string5().oneOf(["eq", "neq", "startswith", "endswith", "in"]).required(fieldErrorMessage),
value: string5().required(fieldErrorMessage)
});
var metricCriteria = object5({
metric: string5().required(fieldErrorMessage),
aggregate_function: string5().oneOf(["avg", "count", "max", "min", "sum"]).required(fieldErrorMessage),
operator: string5().oneOf(["eq", "gt", "lt", "gte", "lte"]).required(fieldErrorMessage),
threshold: number4().required(fieldErrorMessage).min(0, "Enter a positive value.").typeError("The value should be a number."),
dimension_filters: array4().of(dimensionFilters.defined()).optional()
});
var triggerConditionValidation = object5({
criteria_condition: string5().oneOf(["ALL"]).required("Criteria condition is required"),
polling_interval_seconds: number4().required(fieldErrorMessage),
evaluation_period_seconds: number4().required(fieldErrorMessage),
trigger_occurrences: number4().required(fieldErrorMessage).positive("Enter a positive value.").typeError("The value should be a number.")
});
var specialStartEndRegex = /^[^a-zA-Z0-9]/;
var createAlertDefinitionSchema = object5({
label: string5().required(fieldErrorMessage).matches(
/^[^*#&+:<>"?@%{}\\\/]+$/,
"Name cannot contain special characters: * # & + : < > ? @ % { } \\ /."
).max(100, "Name must be 100 characters or less.").test(
"no-special-start-end",
"Name cannot start or end with a special character.",
(value) => {
return !specialStartEndRegex.test(value != null ? value : "");
}
),
description: string5().max(100, "Description must be 100 characters or less.").test(
"no-special-start-end",
"Description cannot start or end with a special character.",
(value) => {
return !specialStartEndRegex.test(value != null ? value : "");
}
).optional(),
severity: number4().oneOf([0, 1, 2, 3]).required(fieldErrorMessage),
rule_criteria: object5({
rules: array4().of(metricCriteria).min(1, "At least one metric criteria is required.").required()
}).required(),
trigger_conditions: triggerConditionValidation,
channel_ids: array4().of(number4().required()).required().min(1, "At least one notification channel is required."),
tags: array4().of(string5().defined()).optional(),
entity_ids: array4().of(string5().defined()).optional(),
regions: array4().of(string5().defined()).optional(),
scope: string5().oneOf(["entity", "region", "account"]).nullable().optional()
});
var editAlertDefinitionSchema = object5({
channel_ids: array4().of(number4().required()).optional(),
label: string5().matches(
/^[^*#&+:<>"?@%{}\\/]+$/,
"Name cannot contain special characters: * # & + : < > ? @ % { } \\ /."
).max(100, "Name must be 100 characters or less.").test(
"no-special-start-end",
"Name cannot start or end with a special character.",
(value) => {
return !specialStartEndRegex.test(value != null ? value : "");
}
).optional(),
description: string5().max(100, "Description must be 100 characters or less.").test(
"no-special-start-end",
"Description cannot start or end with a special character.",
(value) => {
return !specialStartEndRegex.test(value != null ? value : "");
}
).optional(),
entity_ids: array4().of(string5().defined()).optional(),
rule_criteria: object5({
rules: array4().of(metricCriteria).min(1, "At least one metric criteria is required.").required()
}).optional().default(void 0),
tags: array4().of(string5().defined()).optional(),
trigger_conditions: triggerConditionValidation.optional().default(void 0),
severity: number4().oneOf([0, 1, 2, 3]).optional(),
status: string5().oneOf([
"enabled",
"disabled",
"in progress",
"failed",
"provisioning",
"disabling",
"enabling"
]).optional(),
scope: string5().oneOf(["entity", "region", "account"]).nullable().optional(),
regions: array4().of(string5().defined()).optional()
});
// src/databases.schema.ts
import { boolean as boolean4, mixed as mixed2, number as number5 } from "yup";
import { array as array5, object as object6, string as string6 } from "yup";
var LABEL_MESSAGE = "Label must be between 3 and 32 characters";
var createDatabaseSchema = object6({
label: string6().required("Label is required").min(3, LABEL_MESSAGE).max(32, LABEL_MESSAGE),
engine: string6().required("Database Engine is required"),
region: string6().required("Region is required"),
type: string6().required("Type is required"),
cluster_size: number5().oneOf([1, 2, 3], "Nodes are required").required("Nodes are required")
});
var getDynamicDatabaseSchema = (isVPCSelected) => {
if (!isVPCSelected) {
return createDatabaseSchema;
}
return createDatabaseSchema.shape({
private_network: object6().shape({
subnet_id: number5().nullable().required("Subnet is required.")
})
});
};
var updateMaintenanceSchema = object6({
frequency: string6().oneOf(["weekly", "monthly"]).optional(),
hour_of_day: number5(),
day_of_week: number5(),
week_of_month: number5().nullable()
});
var updateDatabaseSchema = object6({
label: string6().notRequired().min(3, LABEL_MESSAGE).max(32, LABEL_MESSAGE),
allow_list: array5().of(string6()).notRequired(),
updates: object6().notRequired().shape({
frequency: string6().oneOf(["weekly", "monthly"]),
duration: number5(),
hour_of_day: number5(),
day_of_week: number5(),
week_of_month: number5().nullable()
}).nullable(),
type: string6().notRequired()
});
var updatePrivateNetworkSchema = object6({
private_network: object6().shape({
vpc_id: number5().required("VPC is required."),
subnet_id: number5().required("Subnet is required."),
public_access: boolean4().default(false)
})
});
var createValidator = (key, field) => {
const fieldTypes = Array.isArray(field.type) ? field.type : [field.type];
switch (true) {
case fieldTypes.includes("integer"):
return number5().transform((val, originalVal) => originalVal === "" ? void 0 : val).integer(`${key} must be a whole number`).required(`${key} is required`);
case fieldTypes.includes("number"):
return number5().transform((val, originalVal) => originalVal === "" ? void 0 : val).required(`${key} is required`);
case fieldTypes.includes("string"):
return string6();
case fieldTypes.includes("boolean"):
return boolean4();
default:
return null;
}
};
var applyConstraints = (validator, key, field) => {
if (!validator) return null;
if (field.minimum !== void 0) {
validator = validator.min(
field.minimum,
`${key} must be at least ${field.minimum}`
);
}
if (field.maximum !== void 0) {
if (field.maximum > Number.MAX_SAFE_INTEGER) {
validator = validator.max(
Number.MAX_SAFE_INTEGER,
`${key} must be at most ${Number.MAX_SAFE_INTEGER}`
);
} else {
validator = validator.max(
field.maximum,
`${key} must be at most ${field.maximum}`
);
}
}
if (field.minLength !== void 0) {
validator = validator.min(
field.minLength,
`${key} must be at least ${field.minLength} characters`
);
}
if (field.maxLength !== void 0) {
validator = validator.max(
field.maxLength,
`${key} must be at most ${field.maxLength} characters`
);
}
if (field.pattern) {
const pattern = field.pattern;
if (key === "default_time_zone") {
validator = validator.matches(
new RegExp(pattern),
`${key} must be an IANA timezone, 'SYSTEM', or a valid UTC offset (e.g., '+03:00')`
);
} else {
validator = validator.matches(
new RegExp(pattern),
`Please ensure that ${key} follows the format ${field.example}`
);
}
}
if (key === "wal_sender_timeout") {
validator = validator.test(
"is-zero-or-in-range",
`${key} must be 0 or between 5000 and 10800000`,
(value) => {
if (typeof value !== "number") return false;
return value === 0 || value >= 5e3 && value <= 108e5;
}
);
}
if (key === "timezone") {
if (!field.value) {
validator = validator.required("timezone cannot be empty");
}
}
if (key === "net_buffer_length") {
validator = validator.test(
"is-multiple-of-1024",
`${key} must be a multiple of 1024`,
(value) => {
if (typeof value !== "number") return false;
return value % 1024 === 0;
}
);
}
return validator;
};
var processField = (schemaShape, field) => {
if (!field.label || !field.type) {
return;
}
const key = field.label;
let validator = createValidator(key, field);
validator = applyConstraints(validator, key, field);
if (validator) {
schemaShape[key] = object6().shape({ value: validator });
}
};
var createDynamicAdvancedConfigSchema = (allConfigurations) => {
if (!Array.isArray(allConfigurations) || allConfigurations.length === 0) {
return object6().shape({});
}
const schemaShape = {};
allConfigurations.forEach((field) => processField(schemaShape, field));
return object6().shape({
configs: array5().of(
object6({
label: string6().required(),
value: mixed2().when("label", (label, schema) => {
var _a, _b;
if (Array.isArray(label)) {
label = label[0];
}
if (typeof label !== "string" || !schemaShape[label]) {
return schema;
}
const valueSchema = (_b = (_a = schemaShape[label]) == null ? void 0 : _a.fields) == null ? void 0 : _b.value;
return valueSchema ? valueSchema : schema;
})
})
)
});
};
var createDatabaseConnectionPoolSchema = object6({
database: string6().required("Database is required"),
mode: string6().oneOf(["transaction", "session", "statement"], "Pool mode is required").required("Pool mode is required"),
label: string6().required("Name is required").max(63, "Name must not exceed 63 characters"),
size: number5().required("Size is required"),
username: string6().nullable().required("Username is required")
});
var updateDatabaseConnectionPoolSchema = object6({
database: string6().optional(),
mode: string6().oneOf(["transaction", "session", "statement"]).optional(),
size: number5().optional(),
username: string6().nullable().optional()
});
// src/delivery.schema.ts
import { array as array6, boolean as boolean5, lazy, mixed as mixed3, number as number6, object as object7, string as string7 } from "yup";
var maxLength = 255;
var maxLengthMessage = "Length must be 255 characters or less.";
var authenticationDetailsSchema = object7({
basic_authentication_user: string7().max(maxLength, maxLengthMessage).required(),
basic_authentication_password: string7().max(maxLength, maxLengthMessage).required()
});
var authenticationSchema = object7({
type: string7().oneOf(["basic", "none"]).required(),
details: mixed3().defined().when("type", {
is: "basic",
then: () => authenticationDetailsSchema.required(),
otherwise: () => mixed3().nullable().test(
"null-or-undefined",
"For type `none` details should be `null` or `undefined`.",
(value) => !value
)
})
});
var clientCertificateDetailsSchema = object7({
tls_hostname: string7().max(maxLength, maxLengthMessage).required(),
client_ca_certificate: string7().required(),
client_certificate: string7().required(),
client_private_key: string7().required()
});
var customHeaderSchema = object7({
name: string7().max(maxLength, maxLengthMessage).required(),
value: string7().max(maxLength, maxLengthMessage).required()
});
var customHTTPsDetailsSchema = object7({
authentication: authenticationSchema.required(),
client_certificate_details: clientCertificateDetailsSchema.optional(),
content_type: string7().oneOf(["application/json", "application/json; charset=utf-8"]).required(),
custom_headers: array6().of(customHeaderSchema).min(1).optional(),
data_compression: string7().oneOf(["gzip", "None"]).required(),
endpoint_url: string7().max(maxLength, maxLengthMessage).required()
});
var akamaiObjectStorageDetailsBaseSchema = object7({
host: string7().max(maxLength, maxLengthMessage).required("Host is required."),
bucket_name: string7().required("Bucket name is required.").min(3, "Bucket name must be between 3 and 63 characters.").matches(/^\S*$/, "Bucket name must not contain spaces.").matches(
/^[a-z0-9].*[a-z0-9]$/,
"Bucket name must start and end with a lowercase letter or number."
).matches(
/^(?!.*[.-]{2})[a-z0-9.-]+$/,
"Bucket name must contain only lowercase letters, numbers, periods (.), and hyphens (-). Adjacent periods and hyphens are not allowed."
).max(63, "Bucket name must be between 3 and 63 characters."),
path: string7().max(maxLength, maxLengthMessage).defined(),
access_key_id: string7().max(maxLength, maxLengthMessage).required("Access Key ID is required."),
access_key_secret: string7().max(maxLength, maxLengthMessage).required("Secret Access Key is required.")
});
var akamaiObjectStorageDetailsPayloadSchema = akamaiObjectStorageDetailsBaseSchema.shape({
path: string7().max(maxLength, maxLengthMessage).optional()
});
var destinationSchemaBase = object7().shape({
label: string7().max(maxLength, maxLengthMessage).required("Destination name is required."),
type: string7().oneOf(["akamai_object_storage", "custom_https"]).required(),
details: mixed3().defined().required().when("type", {
is: "akamai_object_storage",
then: () => akamaiObjectStorageDetailsBaseSchema,
otherwise: () => customHTTPsDetailsSchema
})
});
var destinationFormSchema = destinationSchemaBase;
var createDestinationSchema = destinationSchemaBase.shape({
details: mixed3().defined().required().when("type", {
is: "akamai_object_storage",
then: () => akamaiObjectStorageDetailsPayloadSchema,
otherwise: () => customHTTPsDetailsSchema
})
});
var updateDestinationSchema = createDestinationSchema.omit(["type"]).shape({
details: lazy((value) => {
if ("bucket_name" in value) {
return akamaiObjectStorageDetailsPayloadSchema.noUnknown(
"Object contains unknown fields for Akamai Object Storage Details."
);
}
if ("client_certificate_details" in value) {
return customHTTPsDetailsSchema.noUnknown(
"Object contains unknown fields for Custom HTTPS Details."
);
}
return mixed3().test({
name: "details-schema",
message: "Details object does not match any known schema.",
test: () => false
});
})
});
var clusterRequiredMessage = "At least one cluster must be selected.";
var streamDetailsBase = object7({
cluster_ids: array6().of(number6().defined(clusterRequiredMessage)).when("is_auto_add_all_clusters_enabled", {
is: false,
then: (schema) => schema.min(1, clusterRequiredMessage).required(clusterRequiredMessage)
}),
is_auto_add_all_clusters_enabled: boolean5()
});
var streamDetailsSchema = streamDetailsBase.test(
"cluster_ids-or-all_clusters_enabled",
"Either cluster_ids or is_auto_add_all_clusters_enabled should be set.",
(value) => {
const HasClusterIds = "cluster_ids" in value;
const HasAllClustersEnabled = "is_auto_add_all_clusters_enabled" in value;
return HasClusterIds !== HasAllClustersEnabled;
}
);
var detailsShouldNotExistOrBeNull = (schema) => schema.nullable().test(
"details-should-not-exist",
"Details should be null or no details passed for type `audit_logs`",
(value, ctx) => !("details" in ctx) || value === null
);
var streamSchemaBase = object7({
label: string7().min(3, "Stream name must have at least 3 characters").max(maxLength, maxLengthMessage).required("Stream name is required."),
status: mixed3().oneOf(["active", "inactive"]),
type: string7().oneOf(["audit_logs", "lke_audit_logs"]).required("Stream type is required."),
destinations: array6().of(number6().defined()).ensure().min(1).required(),
details: mixed3().when("type", {
is: "lke_audit_logs",
then: () => streamDetailsSchema.required(),
otherwise: detailsShouldNotExistOrBeNull
})
});
var createStreamSchema = streamSchemaBase;
var updateStreamSchema = streamSchemaBase.omit(["type"]).shape({
status: mixed3().oneOf(["active", "inactive"]).required(),
details: lazy((value) => {
if (value && typeof value === "object" && ("cluster_ids" in value || "is_auto_add_all_clusters_enabled" in value)) {
return streamDetailsSchema.required();
}
return detailsShouldNotExistOrBeNull(mixed3());
})
}).noUnknown("Object contains unknown fields");
var streamAndDestinationFormSchema = object7({
stream: streamSchemaBase.shape({
destinations: array6().of(number6().required()).required(),
details: mixed3().when("type", {
is: "lke_audit_logs",
then: () => streamDetailsBase.required(),
otherwise: (schema) => schema.nullable().equals([null], "Details must be null for audit_logs type")
})
}),
destination: destinationFormSchema.defined().when("stream.destinations", {
is: (value) => !(value == null ? void 0 : value.length),
then: (schema) => schema,
otherwise: (schema) => schema.shape({
details: mixed3().strip()
})
})
});
// src/domains.schema.ts
import { array as array7, mixed as mixed4, number as number7, object as object8, string as string8 } from "yup";
var importZoneSchema = object8({
domain: string8().required("Domain is required."),
remote_nameserver: string8().required("Remote nameserver is required.")
});
var domainSchemaBase = object8().shape({
domain: string8().matches(
// eslint-disable-next-line sonarjs/slow-regex
/([a-zA-Z0-9-_]+\.)+([a-zA-Z]{2,3}\.)?([a-zA-Z]{2,16}|XN--[a-zA-Z0-9]+)/,
"Domain is not valid."
),
status: mixed4().oneOf(["disabled", "active", "edit_mode", "has_errors"]),
tags: array7(),
description: string8().min(1, "Description must be between 1 and 253 characters.").max(253, "Description must be between 1 and 253 characters."),
retry_sec: number7(),
master_ips: array7().of(string8()),
axfr_ips: array7().of(string8()).typeError("Must be a comma-separated list of IP addresses."),
expire_sec: number7(),
refresh_sec: number7(),
ttl_sec: number7()
});
var createDomainSchema = domainSchemaBase.shape({
domain: string8().required("Domain is required.").matches(
// eslint-disable-next-line sonarjs/slow-regex
/([a-zA-Z0-9-_]+\.)+([a-zA-Z]{2,3}\.)?([a-zA-Z]{2,16}|XN--[a-zA-Z0-9]+)/,
"Domain is not valid."
),
tags: array7().of(string8()),
type: mixed4().required().oneOf(["master", "slave"]),
soa_email: string8().when("type", {
is: "master",
then: (schema) => schema.required("SOA Email is required.")
}).email("SOA Email is not valid.").trim(),
master_ips: array7().of(string8()).when("type", {
is: "slave",
then: (schema) => schema.compact().ensure().required("At least one primary IP address is required.").min(1, "At least one primary IP address is required.")
})
});
var updateDomainSchema = domainSchemaBase.shape({
domainId: number7(),
soa_email: string8().email("SOA Email is not valid."),
axfr_ips: array7().of(string8()),
tags: array7().of(string8())
});
// src/firewalls.schema.ts
import ipaddr from "ipaddr.js";
import { array as array8, number as number8, object as object9, string as string9 } from "yup";
var IP_ERROR_MESSAGE = "Must be a valid IPv4 or IPv6 address or range.";
var validateIP = (ipAddress2) => {
if (ipAddress2 !== "" && !ipAddress2) {
return false;
}
const [, mask] = ipAddress2.split("/");
try {
if (mask) {
ipaddr.parseCIDR(ipAddress2);
} else {
ipaddr.parse(ipAddress2);
}
} catch (err) {
if (ipAddress2 !== "") {
return false;
}
}
return true;
};
var CreateFirewallDeviceSchema = object9({
linodes: array8().of(number8()),
nodebalancers: array8().of(number8())
});
var ipAddress = string9().defined().test({
name: "validateIP",
message: IP_ERROR_MESSAGE,
test: validateIP
});
var CUSTOM_PORTS_ERROR_MESSAGE = "";
var validatePort = (port) => {
CUSTOM_PORTS_ERROR_MESSAGE = "Ports must be an integer, range of integers, or a comma-separated list of integers.";
if (!port) {
CUSTOM_PORTS_ERROR_MESSAGE = "Must be 1-65535";
return false;
}
const convertedPort = parseInt(port, 10);
if (!(1 <= convertedPort && convertedPort <= 65535)) {
CUSTOM_PORTS_ERROR_MESSAGE = "Must be 1-65535";
return false;
}
if (port.startsWith("0")) {
CUSTOM_PORTS_ERROR_MESSAGE = "Port must not have leading zeroes";
return false;
}
if (String(convertedPort) !== port) {
return false;
}
return true;
};
var isCustomPortsValid = (ports) => {
const portList = (ports == null ? void 0 : ports.split(",")) || [];
let portLimitCount = 0;
for (const port of portList) {
const cleanedPort = port.trim();
if (cleanedPort.includes("-")) {
const portRange = cleanedPort.split("-");
if (!validatePort(portRange[0]) || !validatePort(portRange[1])) {
return false;
}
if (portRange.length !== 2) {
CUSTOM_PORTS_ERROR_MESSAGE = "Ranges must have 2 values";
return false;
}
if (parseInt(portRange[0], 10) >= parseInt(portRange[1], 10)) {
CUSTOM_PORTS_ERROR_MESSAGE = "Range must start with a smaller number and end with a larger number";
return false;
}
portLimitCount += 2;
} else {
if (!validatePort(cleanedPort)) {
return false;
}
portLimitCount++;
}
}
if (portLimitCount > 15) {
CUSTOM_PORTS_ERROR_MESSAGE = "Number of ports or port range endpoints exceeded. Max allowed is 15";
return false;
}
return true;
};
var validateFirewallPorts = string9().test({
name: "firewall-ports",
message: CUSTOM_PORTS_ERROR_MESSAGE,
test: (value) => {
if (!value) {
return false;
}
try {
isCustomPortsValid(value);
} catch (err) {
return false;
}
return true;
}
});
var FirewallRuleTypeSchema = object9().shape({
action: string9().oneOf(["ACCEPT", "DROP"]).nullable(),
description: string9().nullable(),
label: string9().nullable(),
protocol: string9().oneOf(["ALL", "TCP", "UDP", "ICMP", "IPENCAP"]).nullable(),
ports: string9().when("protocol", {
is: (val) => val !== "ICMP" && val !== "IPENCAP",
then: () => validateFirewallPorts,
// Workaround to get the test to fail if ports is defined when protocol === ICMP or IPENCAP
otherwise: (schema) => schema.test({
name: "protocol",
message: "Ports are not allowed for ICMP and IPENCAP protocols.",
test: (value) => typeof value === "undefined"
})
}).nullable(),
addresses: object9().shape({
ipv4: array8().of(ipAddress).nullable(),
ipv6: array8().of(ipAddress).nullable()
}).strict(true).notRequired().nullable(),
ruleset: number8().nullable()
});
var FirewallRuleSchema = object9().shape({
inbound: array8(FirewallRuleTypeSchema).nullable(),
outbound: array8(FirewallRuleTypeSchema).nullable(),
inbound_policy: string9().oneOf(["ACCEPT", "DROP"]).required("Inbound policy is required."),
outbound_policy: string9().oneOf(["ACCEPT", "DROP"]).required("Outbound policy is required.")
});
var CreateFirewallDevicesSchema = object9().shape({
linodes: array8().of(number8().defined()),
nodebalancers: array8().of(number8().defined()),
linode_interfaces: array8().of(number8().defined())
}).notRequired();
var CreateFirewallSchema = object9().shape({
label: string9().required("Label is required.").min(3, "Label must be between 3 and 32 characters.").max(32, "Label must be between 3 and 32 characters."),
// Label validation on the back end is more complicated, we only do basic checks here.
tags: array8().of(string9().defined()),
rules: FirewallRuleSchema,
devices: CreateFirewallDevicesSchema
});
var UpdateFirewallSchema = object9().shape({
label: string9(),
tags: array8().of(string9()),
status: string9().oneOf(["enabled", "disabled"])
// 'deleted' is also a status but it's not settable
});
var FirewallDeviceSchema = object9({
type: string9().oneOf(["linode", "nodebalancer", "linode_interface"]).required("Device type is required."),
id: number8().required("ID is required.")
});
var UpdateFirewallSettingsSchema = object9({
default_firewall_ids: object9({
interface_public: number8().nullable(),
interface_vpc: number8().nullable(),
linode: number8().nullable(),
nodebalancer: number8().nullable()
})
});
// src/images.schema.ts
import { array as array9, boolean as boolean6, number as number9, object as object10, string as string10 } from "yup";
var labelSchema = string10().min(1, "Label must be between 1 and 50 characters.").max(50, "Label must be between 1 and 50 characters.").matches(
/^[a-zA-Z0-9,.?\-_\s']+$/,
"Image labels cannot contain special characters."
);
var baseImageSchema = object10({
label: labelSchema.optional(),
description: string10().optional().min(1).max(65e3),
cloud_init: boolean6().optional(),
tags: array9(string10().min(3).max(50).required()).max(500).optional()
});
var createImageSchema = baseImageSchema.shape({
disk_id: number9().typeError("Disk is required.").required("Disk is required.")
});
var uploadImageSchema = baseImageSchema.shape({
label: labelSchema.required("Label is required."),
region: string10().required("Region is required.")
});
var updateImageSchema = object10({
label: labelSchema.optional(),
description: string10().optional().max(65e3, "Length must be 65000 characters or less."),
tags: array9(string10().required()).optional()
});
var updateImageRegionsSchema = object10({
regions: array9(string10()).required("Regions are required.").min(1, "Must specify at least one region.")
});
var sharegroupImageSchema = object10({
id: string10().required("Image ID is required"),
label: labelSchema.optional(),
description: string10().optional()
});
var addSharegroupImagesSchema = object10({
images: array9(sharegroupImageSchema).required("Images are required.")
});
var updateSharegroupImageSchema = object10({
label: labelSchema.optional(),
description: string10().optional()
});
var createSharegroupSchema = object10({
label: labelSchema.required("Label is required."),
description: string10().optional(),
images: array9(sharegroupImageSchema).notRequired()
});
var updateSharegroupSchema = object10({
label: labelSchema.optional(),
description: string10().optional()
});
var addSharegroupMemberSchema = object10({
token: string10().required("Token is required."),
label: labelSchema.required("Label is required.")
});
var updateSharegroupMemberSchema = object10({
label: labelSchema.required("Label is required.")
});
var generateSharegroupTokenSchema = object10({
label: labelSchema.optional(),
valid_for_sharegroup_uuid: boolean6().required(
"Valid sharegroup UUID required."
)
});
var updateSharegroupTokenSchema = object10({
label: labelSchema.required("Label is required.")
});
// src/kubernetes.schema.ts
import { array as array10, boolean as boolean7, number as number10, object as object11, string as string11 } from "yup";
var alphaNumericValidCharactersRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-._]*[a-zA-Z0-9])?$/;
var kubernetesTaintSchema = object11({
key: string11().required("Key is required.").test(
"valid-key",
"Key must start with a letter or number and may contain letters, numbers, hyphens, dots, and underscores, up to 253 characters.",
(value) => {
return alphaNumericValidCharactersRegex.test(value) || dnsKeyRegex.test(value);
}
).max(253, "Key must be between 1 and 253 characters.").min(1, "Key must be between 1 and 253 characters."),
value: string11().matches(
alphaNumericValidCharactersRegex,
"Value must start with a letter or number and may contain letters, numbers, hyphens, dots, and underscores, up to 63 characters."
).max(63, "Value must be between 0 and 63 characters.").notOneOf(
["kubernetes.io", "linode.com"],
'Value cannot be "kubernetes.io" or "linode.com".'
).notRequired()
});
var NodePoolDiskSchema = object11({
size: number10().required(),
type: string11().oneOf(["raw", "ext4"]).required()
});
var AutoscaleSettingsSchema = object11({
enabled: boolean7().required(),
max: number10().required(),
min: number10().required()
});
var CreateNodePoolSchema = object11({
autoscaler: AutoscaleSettingsSchema.notRequired().default(void 0),
type: string11().required("Type is required."),
count: number10().required(),
tags: array10(string11().defined()).notRequired(),
disks: array10(NodePoolDiskSchema).notRequired(),
update_strategy: string11().oneOf(["rolling_update", "on_recycle"]).notRequired(),
k8_version: string11().notRequired(),
firewall_id: number10().notRequired(),
labels: object11().notRequired(),
taints: array10(kubernetesTaintSchema).notRequired()
});
var EditNodePoolSchema = object11({
type: string11(),
count: number10(),
update_strategy: string11(),
k8_version: string11(),
firewall_id: number10()
});
var clusterLabelSchema = string11().required("Label is required.").matches(
/^[a-zA-Z0-9-]+$/,
"Cluster labels cannot contain special characters, spaces, or underscores."
).min(3, "Length must be between 3 and 32 characters.").max(32, "Length must be between 3 and 32 characters.");
var ipv4Address = string11().defined().test({
name: "validateIP",
message: "Must be a valid IPv4 address.",
test: validateIP
});
var ipv6Address = string11().defined().test({
name: "validateIP",
message: "Must be a valid IPv6 address.",
test: validateIP
});
var controlPlaneACLOptionsSchema = object11({
enabled: boolean7(),
"revision-id": string11(),
addresses: object11({
ipv4: array10().of(ipv4Address).nullable(),
ipv6: array10().of(ipv6Address).nullable()
}).notRequired()
});
var controlPlaneEnterpriseACLOptionsSchema = object11({
enabled: boolean7(),
"revision-id": string11(),
addresses: object11({
ipv4: array10().of(ipv4Address),
ipv6: array10().of(ipv6Address)
}).required()
});
var createKubeClusterSchema = object11({
label: clusterLabelSchema,
region: string11().required("Region is required."),
k8s_version: string11().required("Kubernetes version is required."),
node_pools: array10().of(CreateNodePoolSchema).min(1, "Please add at least one node pool.")
});
var createKubeClusterWithRequiredACLSchema = object11({
control_plane: object11({
high_availability: boolean7(),
acl: object11({
enabled: boolean7(),
"revision-id": string11(),
addresses: object11({
ipv4: array10().of(ipv4Address),
ipv6: array10().of(ipv6Address)
})
})
}).test(
"validateIPForEnterprise",
"At least one IP address or CIDR range is required for LKE Enterprise.",
function(controlPlane) {
const { ipv4, ipv6 } = controlPlane.acl.addresses;
return ipv4 && ipv4.length > 0 || ipv6 && ipv6.length > 0;
}
).required()
});
var kubernetesControlPlaneACLPayloadSchema = object11({
acl: controlPlaneACLOptionsSchema
});
var kubernetesEnterpriseControlPlaneACLPayloadSchema = object11({
acl: controlPlaneEnterpriseACLOptionsSchema.test(
"validateIPForEnterprise",
"At least one IP address or CIDR range is required for LKE Enterprise.",
function(acl) {
const { ipv4, ipv6 } = acl.addresses || {};
return ipv4 && ipv4.length > 0 && ipv4[0] !== "" || ipv6 && ipv6.length > 0 && ipv6[0] !== "";
}
)
});
var dnsKeyRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-._/]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$/;
var MAX_DNS_KEY_TOTAL_LENGTH = 128;
var MAX_DNS_KEY_SUFFIX_LENGTH = 62;
var MAX_SIMPLE_KEY_OR_VALUE_LENGTH = 63;
var validateKubernetesLabel = (labels) => {
if (!labels) {
return false;
}
for (const [labelKey, labelValue] of Object.entries(labels)) {
if (!labelKey || !labelValue) {
return false;
}
if (labelKey.includes("kubernetes.io") || labelKey.includes("linode.com")) {
return false;
}
if (labelKey.includes("/")) {
const suffix = labelKey.split("/")[0];
if (!dnsKeyRegex.test(labelKey)) {
return false;
}
if (labelKey.length > MAX_DNS_KEY_TOTAL_LENGTH || suffix.length > MAX_DNS_KEY_SUFFIX_LENGTH) {
return false;
}
} else {
if (labelKey.length > MAX_SIMPLE_KEY_OR_VALUE_LENGTH || !alphaNumericValidCharactersRegex.test(labelKey)) {
return false;
}
}
if (labelValue.length > MAX_SIMPLE_KEY_OR_VALUE_LENGTH || !alphaNumericValidCharactersRegex.test(labelValue)) {
return false;
}
}
return true;
};
var kubernetesLabelSchema = object11().test({
name: "validateLabels",
message: "Labels must be valid key-value pairs.",
test: validateKubernetesLabel
});
// src/linodes.schema.ts
import ipaddr3 from "ipaddr.js";
import { array as array12, boolean as boolean8, lazy as lazy3, mixed as mixed5, number as number11, object as object13, string as string13 } from "yup";
// src/vpcs.schema.ts
import ipaddr2 from "ipaddr.js";
import { array as array11, lazy as lazy2, object as object12, string as string12 } from "yup";
var LABEL_MESSAGE2 = "Label must be between 1 and 64 characters.";
var LABEL_REQUIRED = "Label is required.";
var LABEL_REQUIREMENTS = "Label must include only ASCII letters, numbers, and dashes.";
var labelTestDetails = {
testName: "no two dashes in a row",
testMessage: "Label must not contain two dashes in a row."
};
var IP_EITHER_BOTH_NOT_NEITHER = "A subnet must have either IPv4 or IPv6, or both, but not neither.";
var TEMPORARY_IPV4_REQUIRED_MESSAGE = "A subnet must have an IPv4 range.";
var determineIPType = (ip) => {
try {
let addr;
const [, mask] = ip.split("/");
if (mask) {
const parsed = ipaddr2.parseCIDR(ip);
addr = parsed[0];
} else {
addr = ipaddr2.parse(ip);
}
return addr.kind();
} catch (e) {
return void 0;
}
};
var vpcsValidateIP = ({
value,
shouldHaveIPMask,
mustBeIPMask,
checkIPv6PrefixLengthIs64
}) => {
if (!value) {
return false;
}
const [, mask] = value.trim().split("/");
if (mustBeIPMask) {
const valueIsMaskOnly = value === `/${mask}`;
return !mask ? false : ipaddr2.IPv6.subnetMaskFromPrefixLength(Number(mask)) !== null && valueIsMaskOnly && Number(mask) >= 64 && Number(mask) <= 125;
}
try {
const type = determineIPType(value);
const isIPv4 = type === "ipv4";
const isIPv6 = type === "ipv6";
if (!isIPv4 && !isIPv6) {
return false;
}
if (isIPv4) {
if (shouldHaveIPMask) {
ipaddr2.IPv4.parseCIDR(value);
} else {
ipaddr2.IPv4.isValid(value);
ipaddr2.IPv4.parse(value);
}
}
if (isIPv6) {
if (checkIPv6PrefixLengthIs64) {
return mask === "64";
}
if (shouldHaveIPMask) {
ipaddr2.IPv6.parseCIDR(value);
} else {
ipaddr2.IPv6.isValid(value);
ipaddr2.IPv6.parse(value);
}
}
return true;
} catch (err) {
return false;
}
};
var labelValidation = string12().test(
labelTestDetails.testName,
labelTestDetails.testMessage,
(value) => !(value == null ? void 0 : value.includes("--"))
).min(1, LABEL_MESSAGE2).max(64, LABEL_MESSAGE2).matches(/^[a-zA-Z0-9-]*$/, LABEL_REQUIREMENTS);
var updateVPCSchema = object12({
label: labelValidation,
description: string12()
});
var VPCIPv6Schema = object12({
range: string12().optional().test(
"IPv6 prefix length",
"Must be the prefix length 52, 48, or 44 of the IP, e.g. /52",
(value) => {
if (value && value.length > 0) {
return ["/44", "/48", "/52"].includes(value);
}
}
)
});
var VPCIPv6SubnetSchema = object12({
range: string12().required().test(
"IPv6 prefix length",
"Must be the prefix length (52-62) of the IP, e.g. /52",
(value) => {
if (value && value !== "auto" && value.length > 0) {
const [, mask] = value.split("/");
return +mask >= 52 && +mask <= 62;
}
}
)
});
var createSubnetSchemaIPv4 = object12({
label: labelValidation.required(LABEL_REQUIRED),
ipv4: string12().when("ipv6", {
is: (value) => value === "" || value === null || value === void 0,
then: (schema) => schema.required(TEMPORARY_IPV4_REQUIRED_MESSAGE).test({
name: "IPv4 CIDR format",
message: "The IPv4 range must be in CIDR format.",
test: (value) => vpcsValidateIP({
value,
shouldHaveIPMask: true,
mustBeIPMask: false
})
}),
otherwise: (schema) => lazy2((value) => {
switch (typeof value) {
case "string":
return schema.notRequired().test({
name: "IPv4 CIDR format",
message: "The IPv4 range must be in CIDR format.",
test: (value2) => vpcsValidateIP({
value: value2,
shouldHaveIPMask: true,
mustBeIPMask: false
})
});
case "undefined":
return schema.notRequired().nullable();
default:
return schema.notRequired().nullable();
}
})
})
});
var createSubnetSchemaWithIPv