Skip to content

Utilities

AGS comes with some builtin utilities for common operations.

File functions

Reading files

ts
import { readFile, readFileAsync } from "ags/file"

function readFile(path: string): string
function readFileAsync(path: string): Promise<string>

Writing files

ts
import { writeFile, writeFileAsync } from "ags/file"

function writeFile(path: string, content: string): void
function writeFileAsync(path: string, content: string): Promise<void>

Monitoring files

If path is a directory it will be recursively monitored.

ts
import { monitorFile } from "ags/file"

function monitorFile(
  path: string,
  callback: (file: string, event: Gio.FileMonitorEvent) => void,
): Gio.FileMonitor

Timeouts and Intervals

ts
import { interval, timeout, idle, createPoll } from "ags/time"

You can use JavaScript native setTimeout or setInterval functions which return a GLib.Source instance. Alternatively you can use these functions provided by AGS, which return a Timer instance.

ts
class Timer extends GObject.Object {
  declare $signals: {
    now(): void
    cancelled(): void
  }

  cancel(): void
}

Interval

Will immediately execute the function and every interval millisecond.

ts
function interval(interval: number, callback?: () => void): Timer

Timeout

Will execute the callback after timeout millisecond.

ts
function timeout(timeout: number, callback?: () => void): Timer

Idle

Executes callback whenever there are no higher priority events pending.

ts
function idle(callback?: () => void): Timer

Example:

ts
const timer = interval(1000, () => {
  console.log("optional callback")
})

timer.connect("now", () => {
  console.log("tick")
})

timer.connect("cancelled", () => {
  console.log("cancelled")
})

timer.cancel()

createPoll

createPoll creates a signal that only polls when there is at least one subscriber.

ts
function createPoll(
  init: string,
  interval: number,
  exec: string | string[],
): Accessor<string>

function createPoll<T>(
  init: T,
  interval: number,
  exec: string | string[],
  transform: (stdout: string, prev: T) => T,
): Accessor<T>

function createPoll<T>(
  init: T,
  interval: number,
  fn: (prev: T) => T | Promise<T>,
): Accessor<T>

Example:

tsx
function Counter() {
  const counter = createPoll(0, 1000, (prev) => prev + 1)

  return <label label={counter((c) => c.toString())} />
}

WARNING

Under the hood when passing a command as the third argument it uses execAsync which runs the given programs as is. They are not executed in a shell environment, they do not expand ENV variables like $HOME, and they do not handle logical operators like && and ||.

If you want bash, run them with bash.

ts
createPoll("", 1000, "bash -c 'command $ENV_VAR && command'")

Process functions

Import from ags/process

ts
import { subprocess, exec, execAsync, createSubprocess } from "ags/process"

Subprocess

You can start a subprocess and run callback functions whenever it outputs to stdout or stderr. The returned Process instance has an stdout and stderr signal.

ts
class Process extends GObject.Object {
  declare $signals: {
    stdout(out: string): void
    stderr(err: string): void
    exit(code: number, signaled: boolean): void
  }

  kill(): void
  write(str: string): void
  async writeAsync(str: string): Promise<void>
}

function subprocess(args: {
  cmd: string | string[]
  out?: (stdout: string) => void
  err?: (stderr: string) => void
}): Process

function subprocess(
  cmd: string | string[],
  onOut?: (stdout: string) => void,
  onErr?: (stderr: string) => void,
): Process

Example:

ts
const proc = subprocess(
  "some-command",
  (out) => console.log(out), // optional
  (err) => console.error(err), // optional
)

// or with signals
const proc = subprocess("some-command")
proc.connect("stdout", (_, out) => console.log(out))
proc.connect("stderr", (_, err) => console.error(err))

Executing external commands and scripts

ts
function exec(cmd: string | string[]): string
function execAsync(cmd: string | string[]): Promise<string>

WARNING

You should generally avoid exec and use its async variant execAsync as the former will block IO, meaning the whole shell will freeze and will be unresponsive until it returns.

Example:

ts
try {
  const out = exec("/path/to/script")
  console.log(out)
} catch (err) {
  console.error(err)
}

execAsync(["bash", "-c", "/path/to/script.sh"])
  .then((out) => console.log(out))
  .catch((err) => console.error(err))

WARNING

subprocess, exec, and execAsync executes the passed executable as is. They are not executed in a shell environment, they do not expand ENV variables like $HOME, and they do not handle logical operators like && and ||.

If you want bash, run them with bash.

ts
exec(["bash", "-c", "command $ENV_VAR && command"])
exec("bash -c 'command $ENV_VAR' && command")

createSubprocess

createSubprocess creates a signal that starts a subprocess when the first subscriber appears and kills the subprocess when number of subscribers drop to zero.

ts
export function createSubprocess(
  init: string,
  exec: string | string[],
): Accessor<string>

export function createSubprocess<T>(
  init: T,
  exec: string | string[],
  transform: (stdout: string, prev: T) => T,
): Accessor<T>

Example:

tsx
function Log() {
  const log = createSubprocess("", "journalctl -f")

  return <label label={log} />
}

Http requests

ts
import { fetch, URL } from "ags/fetch"

const url = new URL("https://some-site.com/api")
url.searchParams.set("hello", "world")

const res = await fetch(url, {
  method: "POST",
  body: JSON.stringify({ hello: "world" }),
  headers: {
    "Content-Type": "application/json",
  },
})

const json = await res.json()

Released under the GPL v3.0 License