7.3.292-stable Switch to dev

std > runtime > Source

@permission("public", "default, associated with anonymous users");
@permission("admin", "allows to administrate anything on the server");
@permission("api", "allows access to exposed functions and webroot files");
@permission("debug", "allows access to low-level graph manipulation functions");
@permission("files", "allows access to files under /files/* or webroot according to ACL");

@role("public", "public");
@role("admin", "public", "admin", "api", "debug", "files");
@role("user", "public", "api", "files");

/// Unit of access control, attached to functions
private type Permission {
    name: String;
    description: String;

    @expose
    @permission("admin")
    static native fn all(): Array<Permission>;
}

/// Aggregation of permissions to be associated to a user
private type Role {
    name: String;
    permissions: Array<String>;

    @expose
    @permission("admin")
    static native fn all(): Array<Role>;
}

/// Unit of computation executed in parallel awaited by a parent Task
type Job<T> {
    function: core::function;
    arguments: Array<any?>?;
    native fn result(): T;
}

/// The strategy applies to merge node updates from job to task or task to the main graph.
enum MergeStrategy {
    /// All or nothing. Every node update must be free of conflicts with concurrent changes; otherwise, an exception is thrown.
    /// This should be considered the default mode, as it offers the strongest guarantee of consistency.
    strict;
    /// Partial strategy making merge not crash on concurrent conflicts.
    ///All node updates are applied and conflicts are resolved using the previously inserted values in a graph.
    first_wins;
    /// Partial strategy making merge not crash on concurrent conflicts.
    /// All node updates are applied and conflicts are resolved by overriding previously inserted values by current ones.
    last_wins;
}

native fn await(jobs: Array<Job>, strategy: MergeStrategy);

/// log levels used in log files
enum LogLevel {
    error;
    warn;
    info;
    perf;
    trace;
}

/// log structure useful for parsing
@volatile
type Log {
    level: LogLevel;
    time: time;
    user_id: int?;
    id: int?;
    id2: int?;
    src: function?;
    data: any?;
}

@volatile
type LogDataUsage {
    read_bytes: int;
    read_hits: int;
    read_wasted: int;
    write_bytes: int;
    write_hits: int;
    cache_bytes: int;
    cache_hits: int;
}

@volatile
type RuntimeInfo {
    version: String;
    program_version: String?;
    arch: String;
    timezone: TimeZone;
    license: License;
    io_threads: int;
    bg_threads: int;
    fg_threads: int;
    mem_total: int;
    mem_worker: int;
    disk_data_bytes: int;
}

type Runtime {
    @expose
    @permission("debug")
    static native fn info(): RuntimeInfo;

    @expose
    @reserved
    @permission("api")
    static native fn abi();

    @expose
    @permission("debug")
    static native fn root(): any;

    /// Puts the current thread to sleep for at least the given duration
    static native fn sleep(d: duration);

    /// Perform full backup even if incremental delta are present in backup directory
    static native fn backup_full();

    /// Perform incremental backup based on delta present in backup directory
    static native fn backup_delta();

    /// Trigger a defrag process
    static native fn defrag();
}

/// This type is not supposed to be created manually, use `System::spawn(...)` to create a `ChildProcess`.
///
/// If you do not ensure the `ChildProcess` has exited then it will continue to run, even after the handle
/// has gone out of scope.
type ChildProcess {
    private pid: int;
    /// Waits for the child to exit completely returning `ChildProcessResult` with `code`, `stdout` and `stderr`
    native fn wait(): ChildProcessResult;
    /// Forces the child process to exit.
    native fn kill();
}

type ChildProcessResult {
    /// Return code of the process
    code: int;
    stdout: String;
    stderr: String;
}

type System {
    /// Executes the given command as a subprocess.
    /// Returns the content of stdout.
    /// If an error occurs the exception will contain the content of stderr.
    static native fn exec(path: String, params: Array<String>): String;
    /// Spawns the given command as a subprocess and yields its pid.
    ///
    /// Contrary to `System::exec` this method does not actively wait for the process to finish.
    static native fn spawn(path: String, params: Array<String>): ChildProcess;
    /// get local configured TimeZone
    static native fn tz(): TimeZone;
    static native fn getEnv(key: String): String?;
}

enum TaskStatus {
    empty;
    waiting;
    running;
    await;
    cancelled;
    error;
    ended;
    ended_with_errors;
}

type Task {
    user_id: int;
    task_id: int;
    mod: String?;
    type: String?;
    fun: String?;
    creation: time;
    start: time?;
    duration: duration?;
    status: TaskStatus;
    progress: float?;

    /// set globally the progress for parent task, accepted value are between 0 and 1.
    static native fn progress(progress: float);
    static native fn parentId(): int;
    static native fn id(): int;

    @expose
    @reserved
    static native fn running(): Array<Task>;
    @expose
    @reserved
    static native fn history(offset: int, max: int): Array<Task>;
    @expose
    @reserved
    static native fn cancel(task_id: int): bool;
    @expose
    @reserved
    static native fn is_running(task_id: int): bool;
}

type SecurityFields {
    email: String?;
    name: String?;
    first_name: String?;
    last_name: String?;
    roles: Map<String, String>?;
    groups: Map<String, String>?;

    @expose
    @permission("admin")
    static native fn set(f: SecurityFields);

    @expose
    @permission("admin")
    static native fn get(): SecurityFields?;
}

type SecurityPolicy {
    entities: Array<SecurityEntity>?;
    credentials: Map<String, UserCredential>?;
    fields: SecurityFields?;
    keys: Map<String, String>?;
    keys_last_refresh: time?;
}

abstract type SecurityEntity {
    id: int;
    name: String;
    activated: bool;

    @expose
    @reserved
    @permission("admin")
    static native fn all(): Array<SecurityEntity>;

    @expose
    @permission("admin")
    static native fn set(entity: SecurityEntity): int?;
}

type UserGroup extends SecurityEntity {}

enum UserGroupPolicyType {
    read;
    write;
    execute;
}

type UserGroupPolicy {
    group_id: int;
    type: UserGroupPolicyType;
}

type OpenIDConnect {
    url: String;
    clientId: String;

    /// get current configuration to enable OpenID connect capability
    @reserved
    @expose
    @permission("public")
    static native fn config(): OpenIDConnect?;
}

type User extends SecurityEntity {
    full_name: String?;
    email: String?;
    role: String?;
    groups: Array<UserGroupPolicy>?;
    groups_flags: int?;
    external: bool;

    /// If the given `credentials` are valid, returns a session `token`.
    ///
    /// If `use_cookie` is `true`, the HTTP Response Headers will contain
    /// a `Set-Cookie: greycat=<TOKEN> (...)`
    ///
    /// This token can then be used by HTTP clients:
    /// - as a bearer: `Authorization: <TOKEN>` _(note that 'bearer' is not specified)_
    /// - as a cookie: `Cookie: greycat=<TOKEN>`
    @expose
    @reserved
    @permission("public")
    static native fn login(credentials: String, use_cookie: bool): String;

    /// If the given JWT `token` is valid (signed with the public key provided), returns a Greycat session `token`.
    ///
    /// If `use_cookie` is `true`, the HTTP Response Headers will contain
    /// a `Set-Cookie: greycat=<TOKEN> (...)`
    ///
    /// This token can then be used by HTTP clients:
    /// - as a bearer: `Authorization: <TOKEN>` _(note that 'bearer' is not specified)_
    /// - as a cookie: `Cookie: greycat=<TOKEN>`
    @expose
    @reserved
    @permission("public")
    static native fn tokenLogin(token: String, use_cookie: bool): String;

    /// logout cookie
    @expose
    @reserved
    @permission("public")
    static native fn logout();

    /// renew cookie
    @expose
    @reserved
    @permission("api")
    static native fn renew(use_cookie: bool): String;

    /// Returns the currently logged-in user id.
    @expose
    @reserved
    @permission("public")
    static native fn current(): int;

    /// Returns the currently logged-in user.
    @expose
    @reserved
    @permission("public")
    static native fn me(): User;

    /// Returns the list of permissions of the currently logged-in user.
    @expose
    @reserved
    @permission("public")
    static native fn permissions(): Array<String>;

    /// Returns `true` if the current connected user has the permission associated to the name passed as parameter, false otherwise.
    static native fn hasPermission(permission: String): bool;

    @expose
    @permission("admin")
    /// Updates the password of the user `name`. If the user does not exist, `false` is returned.
    static native fn setPassword(name: String, pass: String): bool;

    static native fn getByName(name: String): User?;

    static native fn get(id: int): User?;

    /// Validates the password of the user `name`, if the user does not exist or the password does not match, `false` is returned.
    static native fn checkPassword(name: String, pass: String): bool;
}

private type UserCredential {
    offset: int;
    pass: String?;
}

enum LicenseType {
    community;
    enterprise;
    testing;
}

type License {
    /// Associated username
    name: String?;
    /// Start of license validity
    start: time;
    /// End of license validity
    end: time;
    /// Associated company name
    company: String?;
    /// Maximum allowed memory in MB
    max_memory: int;
    extra_1: int?;
    extra_2: int?;
    /// type of license
    type: LicenseType?;
}

/// Checkpoint frame variable, named by program origin name if any
private type Variable {
    name: String?;
    value: any?;
}

/// Checkpoint stack frame, representing nested function call
private type Frame {
    module: String?;
    type: String?;
    function: String?;
    src: String?;
    line: int;
    column: int;
    scope: Array<Variable>;
}

/// Checkpoint snapshots, containing frames themselves containing variables
private type Debug {
    id: int;
    frames: Array<Frame>;
    root: any;

    @expose
    @permission("debug")
    @reserved
    native static fn all(): Array<int>;

    @expose
    @permission("debug")
    @reserved
    native static fn get(id: int): Debug;

    @expose
    @permission("debug")
    @reserved
    native static fn resume(id: int);
}

/// A global manager of periodic tasks.
/// 
/// The scheduler maintains a registry of functions and their associated periodic execution rules.
/// Each function can only have one scheduled task at a time - adding a new task with the same
/// function will replace any existing task.
type Scheduler {
    /// Schedules a function to be executed as a task periodically.
    ///
    /// If a task with the same `function` already exists, it will be replaced with the new
    /// configuration. The scheduler uses function pointer equality for task identification.
    ///
    /// Examples:
    /// ```gcl
    /// // Schedule a backup every day at 2 AM
    /// Scheduler::add(
    ///     backup_database,
    ///     DailyPeriodicity { hour: 2 },
    ///     null,
    /// );
    /// 
    /// // Schedule health checks every 5 minutes, starting in 1 hour
    /// Scheduler::add(
    ///     health_check,
    ///     FixedPeriodicity { every: 5min },
    ///     PeriodicOptions { 
    ///         start: time::now() + 1hour,
    ///         max_duration: 30s
    ///     }
    /// );
    /// ```
    @expose
    @permission("admin")
    static native fn add(function: function, periodicity: Periodicity, options: PeriodicOptions?);

    /// Returns the current list of all scheduled tasks.
    /// 
    /// The returned array includes both active and inactive tasks.
    /// Use `PeriodicTask.is_active` to check individual task status.
    @expose
    @permission("admin")
    static native fn list(): Array<PeriodicTask>;

    /// Looks for a task that matches the given `function`.
    /// 
    /// Returns `null` if no matching task is found.
    /// Uses function pointer equality for matching.
    @expose
    @permission("admin")
    static native fn find(function: function): PeriodicTask?;

    /// Tries to find a task that matches `function` and activates it.
    /// 
    /// Activating a task means it will be eligible for execution according to its periodicity.
    /// If the task was previously deactivated, it will resume from its next scheduled time.
    /// 
    /// Returns `true` if a matching task was found and activated, `false` otherwise.
    @expose
    @permission("admin")
    static native fn activate(function: function): bool;

    /// Tries to find a task that matches `function` and deactivates it.
    /// 
    /// Deactivating a task prevents it from being executed, but keeps the task configuration
    /// in the scheduler. The task can be reactivated later with `activate()`.
    /// 
    /// Returns `true` if a matching task was found and deactivated, `false` otherwise.
    @expose
    @permission("admin")
    static native fn deactivate(function: function): bool;

    // /// Removes a task completely from the scheduler.
    // /// 
    // /// Unlike `deactivate()`, this permanently removes the task configuration.
    // /// The function will no longer be scheduled for execution.
    // /// 
    // /// Returns `true` if a matching task was found and removed, `false` otherwise.
    // @expose
    // @permission("admin")
    // static native fn remove(function: function): bool;
}

/// Represents a scheduled periodic task in the system.
type PeriodicTask {
    /// The function that will be executed
    function: function;
    /// The periodicity configuration for this task
    periodicity: Periodicity;
    /// Current options applied to this task
    options: PeriodicOptions;
    /// Whether the task is currently active
    is_active: bool;
    /// Next scheduled execution time
    next_execution: time;
    /// Total number of times this task has been executed
    execution_count: int;
}

/// Configuration options for periodic tasks.
type PeriodicOptions {
    /// Whether or not the task can be executed.
    /// Defaults to `true` if not specified.
    activated: bool?;
    /// Will start the task lifecycle at that time.
    ///
    /// By default `time::now()` is used.
    /// If set to a future time, the first execution will be delayed accordingly.
    start: time?;
    /// The maximum duration a task should take before being forcefully cancelled.
    ///
    /// By default this is `null` meaning the task can run endlessly.
    /// 
    /// *Note that the timer starts as soon as a task is queued for execution.*
    /// *Note: a negative duration will be replaced by `null`*
    max_duration: duration?;
}

/// Base type for all periodicity definitions.
///
/// Cannot be instantiated directly - use one of the concrete implementations.
abstract type Periodicity {}

/// Defines tasks that repeat at fixed intervals.
///
/// Examples:
/// - Every 30 minutes: `FixedPeriodicity { every: 30min }`
/// - Every 2 hours: `FixedPeriodicity { every: 2hour }`
/// - Every day: `FixedPeriodicity { every: 24hour }`
type FixedPeriodicity extends Periodicity {
    /// The fixed interval between task executions
    every: duration;
}

/// Defines tasks daily execution parameters with specific time targeting.
/// All time fields default to 0 if not specified, resulting in midnight execution.
///
/// Examples:
/// - Run at 2:30 PM: `DailyPeriodicity { hour: 14, minute: 30 }`
/// - Run at midnight: `DailyPeriodicity {}` (all defaults)
/// - Run at 9 AM Europe/Luxembourg: `DailyPeriodicity { hour: 9, timezone: TimeZone::"Europe/Luxembourg" }`
type DailyPeriodicity extends Periodicity {
    /// Hour of execution (0-23). Defaults to midnight if not specified
    hour: int?;
    /// Minute of execution (0-59). Defaults to 0 if not specified
    minute: int?;
    /// Second of execution (0-59). Defaults to 0 if not specified
    second: int?;
    /// Timezone for time calculation. Uses the host timezone if not specified
    timezone: TimeZone?;
}

/// Defines tasks that run on specific days of the week.
/// 
/// Examples:
/// - Every Monday and Friday at 9 AM:
///   ```gcl
///   WeeklyPeriodicity {
///     days: [DayOfWeek::Mon, DayOfWeek::Fri],
///     dayly: DailyPeriodicity { hour: 9 }
///   }
///   ```
/// - Every weekday at default time:
///   ```gcl
///   WeeklyPeriodicity {
///     days: [DayOfWeek::Mon, DayOfWeek::Tue, DayOfWeek::Wed, DayOfWeek::Thu, DayOfWeek::Fri]
///   }
///   ```
type WeeklyPeriodicity extends Periodicity {
    /// Array of weekdays when the task should run
    days: Array<DayOfWeek>;
    /// Optional daily periodicity. Defaults to midnight if not provided.
    daily: DailyPeriodicity?;
}

/// Defines tasks that run monthly on specific days.
///
/// Examples:
/// - 15th of every month at 2 PM:
///   ```gcl
///   MonthlyPeriodicity {
///     days: [15],
///     daily: DailyPeriodicity { hour: 14 }
///   }
///   ```
/// - Every first day and last day of the month at midnight:
///   ```gcl
///   MonthlyPeriodicity {
///     days: [1, -1]
///   }
///   ```
/// - Three days a month at 9:30 PM:
///   ```gcl
///   MonthlyPeriodicity {
///     days: [1, 15, -1],
///     daily: DailyPeriodicity { hour: 9, minute: 30 }
///   }
///   ```
type MonthlyPeriodicity extends Periodicity {
    /// Array of days in the month when the task should run.
    /// Positive values (1-31) count from start of month.
    /// Negative values (-1 to -31) count from end of month (-1 = last day).
    /// Invalid days for shorter months are skipped (e.g., day 31 in February).
    days: Array<int>;
    /// Optional daily timing specification. Defaults to midnight if not provided.
    daily: DailyPeriodicity?;
}

/// Defines tasks that run on specific calendar dates each year.
/// 
/// Examples:
/// - New Year's Day and Christmas:
///   ```gcl
///   YearlyPeriodicity {
///     dates: [
///       DateTuple { day: 1, month: Jan },
///       DateTuple { day: 25, month: Dec }
///     ]
///   }
///   ```
/// - Quarterly reports (1st of each quarter):
///   ```gcl
///   YearlyPeriodicity {
///     dates: [
///       DateTuple { day: 1, month: Jan },
///       DateTuple { day: 1, month: Apr },
///       DateTuple { day: 1, month: Jul },
///       DateTuple { day: 1, month: Oct }
///     ]
///   }
///   ```
type YearlyPeriodicity extends Periodicity {
    /// Array of specific dates (day + month) when the task should run
    dates: Array<DateTuple>;
    /// Timezone for date calculation. Uses the host timezone if not specified
    timezone: TimeZone?;
}

enum DayOfWeek {
    Mon(0),
    Tue(1),
    Wed(2),
    Thu(3),
    Fri(4),
    Sat(5),
    Sun(6);
}

enum Month {
    Jan(0),
    Feb(1),
    Mar(2),
    Apr(3),
    May(4),
    Jun(5),
    Jul(6),
    Aug(7),
    Sep(8),
    Oct(9),
    Nov(10),
    Dec(11);
}

/// Represents a specific date within a year.
/// The day field must be valid for the specified month (e.g., February 30th is invalid).
/// Leap year handling is automatic for February dates.
type DateTuple {
    /// Day of the month (1-31). Must be valid for the specified month
    day: int;
    /// Month of the year
    month: Month;
}

type OpenApi {
    /// Returns an `OpenApiV3` specification from the current program.
    /// All exposed functions are exported as paths, with their parameters
    /// and return types represented as schemas.
    @expose
    @permission("api")
    static native fn v3(): OpenApiV3;
}

private type OpenApiV3 {
    openapi: OpenApiVersion;
    info: InfoObject;
    paths: Map<String, PathItemObject>?;
    components: ComponentsObject?;
}

private enum OpenApiVersion {
    "3.0.4";
}

private type InfoObject {
    /// The title of the API
    title: String;
    /// The version of the OpenAPI Document
    version: String;
}

private type PathItemObject {
    description: String?;
    post: OperationObject?;
}

private type OperationObject {
    requestBody: RequestBodyObject?;
    responses: Map<String, ResponseObject>?;
}

private enum ResponseCode {
    "200",
    "400",
    "404"
}

private type RequestBodyObject {
    content: Map<String, MediaTypeObject>;
    /// Determines if the request body is required in the request. Defaults to `false`
    required: bool?;
}

private type MediaTypeObject {
    schema: SchemaObject;
}

private type SchemaObject {
    "$ref": String?;
    type: SchemaType?;
    format: SchemaFormat?;
    nullable: bool?;
    properties: Map<String, SchemaObject>?;
    required: Array<String>?;
    items: SchemaObject?;
    oneOf: Array<SchemaObject>?;
    allOf: Array<SchemaObject>?;
    minItems: int?;
    maxItems: int?;
    enum: Array<String>?;
    additionalProperties: SchemaObject?;
}

private enum SchemaType {
    string,
    number,
    integer,
    boolean,
    object,
    array,
}

private enum SchemaFormat {
    /// number, signed 32 bits
    int32,
    /// number, signed 64 bits (a.k.a long)
    int64,
    /// number, 32 bits
    float,
    /// number, 64 bits
    double,
    /// string, base64 encoded characters [RFC4648 - Section 4](https://datatracker.ietf.org/doc/html/rfc4648#section-4)
    byte,
    /// string, any sequence of bytes
    binary,
    /// string, as defined by `full-date` [RFC3339 - Section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6)
    date,
    /// string, as defined by `date-time` [RFC3339 - Section 5.6](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6)
    "date-time",
    /// A hint to obscure the value
    password,
}

private type ResponseObject {
    description: String;
    headers: Map<String, HeaderObject>?;
    content: Map<String, MediaTypeObject>?;
}

private type HeaderObject {
    /// A brief description of the header
    description: String?;
    /// Determines whether this header is mandatory. The default value is `false`
    required: bool?;
}

private type ComponentsObject {
    schemas: Map<String, SchemaObject>?;
}