@acurast/cli
Version:
A cli to interact with the Acurast Cloud.
204 lines • 8.07 kB
JavaScript
import { z } from 'zod';
import { AssignmentStrategyVariant, RestartPolicy, DeploymentRuntime, RequiredModules, ScriptMutability, MultiOrigin, } from '../types.js';
const isAcurastAddress = (val) => {
// TODO: Add address validation
return true;
};
const isNotAcurastAddressMessage = 'Invalid Acurast address';
const acurastProjectConfigSchema = z
.object({
projectName: z.string(),
fileUrl: z.string(),
entrypoint: z.string().optional(),
network: z.literal('canary'),
onlyAttestedDevices: z.boolean(),
startAt: z
.union([
z.object({ msFromNow: z.number().min(0) }),
z.object({
timestamp: z.union([
z
.number()
.refine((val) => {
// TODO: What if this throws?
const date = new Date(val);
return !isNaN(date.getTime());
}, { message: 'Invalid timestamp' })
.refine((val) => {
return val >= Date.now();
}, { message: 'Timestamp cannot be in the past' }),
z.string().datetime(),
]),
}),
])
.optional(),
assignmentStrategy: z.union([
z.object({
type: z.literal(AssignmentStrategyVariant.Single),
instantMatch: z
.array(z.object({
processor: z
.string()
.refine(isAcurastAddress, isNotAcurastAddressMessage),
maxAllowedStartDelayInMs: z.number().min(0),
}))
.optional(),
}),
z.object({
type: z.literal(AssignmentStrategyVariant.Competing),
}),
]),
execution: z.union([
z
.object({
type: z.literal('onetime'),
maxExecutionTimeInMs: z.number().min(1),
})
.strict(),
z
.object({
type: z.literal('interval'),
intervalInMs: z.number().min(1),
numberOfExecutions: z.number().min(1),
maxExecutionTimeInMs: z.number().min(1).optional(),
})
.strict()
.refine((data) => {
if (data.maxExecutionTimeInMs === undefined)
return true;
return data.maxExecutionTimeInMs < data.intervalInMs;
}, {
message: 'maxExecutionTimeInMs must be less than intervalInMs',
path: ['maxExecutionTimeInMs'],
}),
]),
maxAllowedStartDelayInMs: z.number().min(0),
usageLimit: z.object({
maxMemory: z.number().min(0),
maxNetworkRequests: z.number().min(0),
maxStorage: z.number().min(0),
}),
numberOfReplicas: z.number().min(1).max(64),
requiredModules: z.array(z.nativeEnum(RequiredModules)).optional(),
minProcessorReputation: z.number().min(0),
maxCostPerExecution: z.number().min(0),
includeEnvironmentVariables: z.array(z.string()).optional(),
processorWhitelist: z
.array(z.string().refine(isAcurastAddress, isNotAcurastAddressMessage))
.optional(),
minProcessorVersions: z
.union([
z.object({
android: z.union([z.string(), z.number()]),
ios: z.union([z.string(), z.number()]).optional(),
}),
z.object({
android: z.union([z.string(), z.number()]).optional(),
ios: z.union([z.string(), z.number()]),
}),
z.object({
android: z.union([z.string(), z.number()]),
ios: z.union([z.string(), z.number()]),
}),
])
.optional(),
restartPolicy: z.nativeEnum(RestartPolicy).optional(),
runtime: z.nativeEnum(DeploymentRuntime).optional(),
mutability: z.nativeEnum(ScriptMutability).optional(),
reuseKeysFrom: z
.tuple([z.nativeEnum(MultiOrigin), z.string(), z.number()])
.optional(),
})
.refine((data) => {
// Conditional validation for mutability
if (data.mutability === ScriptMutability.Mutable) {
if (!data.minProcessorVersions) {
return false;
}
let allVersionsValid = true;
// Check Android version
const androidVersion = data.minProcessorVersions.android;
if (androidVersion !== undefined) {
const androidVersionCode = typeof androidVersion === 'number'
? androidVersion
: parseInt(androidVersion, 10);
if (!isNaN(androidVersionCode) && androidVersionCode >= 91) {
// Pass
}
else {
allVersionsValid = false;
}
}
// Check iOS version
const iosVersion = data.minProcessorVersions.ios;
if (iosVersion !== undefined) {
const iosVersionCode = typeof iosVersion === 'number'
? iosVersion
: parseInt(iosVersion, 10);
if (!isNaN(iosVersionCode) && iosVersionCode >= 63353) {
// Pass
}
else {
allVersionsValid = false;
}
}
// At least one platform must have a valid version
if (!allVersionsValid) {
return false;
}
}
return true;
}, {
message: 'When mutability is set to "Mutable", minProcessorVersions must include at least one platform with Android version code 91 or iOS version code 63353.',
path: ['mutability'],
});
const acurastProjectConfigSchemaWithNotes = acurastProjectConfigSchema.superRefine((data, context) => {
if (!data.onlyAttestedDevices) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'Note: onlyAttestedDevices is set to false. This means that the deployment will run on all devices, including unattested devices. This is not recommended for production deployments.',
path: ['onlyAttestedDevices'],
});
}
if (data.startAt) {
if ('msFromNow' in data.startAt) {
if (data.assignmentStrategy.type === AssignmentStrategyVariant.Single &&
!data.assignmentStrategy.instantMatch &&
data.startAt.msFromNow < 300000) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'The start time is less than 5 minutes from now. This can lead to the deployment not running.',
path: ['startAt', 'msFromNow'],
});
}
if (data.assignmentStrategy.type === AssignmentStrategyVariant.Single &&
data.assignmentStrategy.instantMatch &&
data.startAt.msFromNow < 120000) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'The start time is less than 2 minutes from now. Even with an instantMatch provided, this can lead to the deployment not running.',
path: ['startAt', 'msFromNow'],
});
}
}
if ('timestamp' in data.startAt) {
if (data.assignmentStrategy.type === AssignmentStrategyVariant.Single &&
data.assignmentStrategy.instantMatch &&
new Date(data.startAt.timestamp).getTime() - Date.now() < 120000) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: 'The start time is less than 5 minutes from now. This can lead to the deployment not running.',
path: ['startAt', 'timestamp'],
});
}
}
}
// TODO: Add check for competing strategy and intervals
});
// Function to validate the JSON data
export const validateConfig = (config) => {
var _a;
const result = Object.assign(Object.assign({}, acurastProjectConfigSchema.safeParse(config)), { notes: (_a = acurastProjectConfigSchemaWithNotes.safeParse(config).error) === null || _a === void 0 ? void 0 : _a.issues });
return result;
};
//# sourceMappingURL=validateConfig.js.map