Modules (require)

Load a module with require('name'). NodeAmiga looks up modules in this order:

  1. Built-in native modules (implemented in C inside NodeAmiga)
  2. Embedded bundle (when running from a -compile'd standalone executable)
  3. Relative path (./file.js / ../file.js)
  4. Absolute AmigaOS path (anything containing :, e.g. SYS:libs/foo.js)
  5. Search path: PROGDIR:libs/, LIBS:node/, libs/, PROGDIR:modules/, current dir - tries both name.js and name/index.js

ES-module syntax (import / export) is also supported and uses the same resolution logic.

Relative paths with leading ../ are folded by popping a segment off the importing module's directory before the path reaches dos.library (AmigaOS does not natively recognise .. as a path segment - it uses // for parent). At a volume root (e.g. Work:) extra ../ are silently dropped.

If the file is not found in any of the search locations, require() throws Error("Cannot find module 'X' (resolved to '...', from '...')") rather than silently returning undefined. Catch it with try/catch if your module is optional.

Modules on this page

fs - files & directories

var fs = require('fs');

Synchronous filesystem via AmigaOS DOS. All operations take AmigaOS-style paths (RAM:foo.txt, SYS:s/startup-sequence).

fs.readFileSync(path, encoding?)

Returns the file contents. With encoding: 'utf8' returns a string; otherwise a Buffer.

fs.writeFileSync(path, data)

Write a string or Buffer to disk. Overwrites existing files.

fs.appendFileSync(path, data)

Append to an existing file (or create).

fs.existsSync(path) → boolean
fs.unlinkSync(path)

Delete a file.

fs.renameSync(oldPath, newPath)
fs.statSync(path)

Returns {size, isFile(), isDirectory()}. Throws Error with ENOENT if the path doesn't exist.

fs.readdirSync(path)

Returns an array of entry names (no . / ..). Uses Lock+Examine+ExNext.

fs.mkdirSync(path) / fs.rmdirSync(path)
fs.copyFileSync(src, dst)

4 KB buffered copy.

fs.readFile(path, callback) / fs.writeFile(path, data, callback)

Callback-style versions. Still synchronous internally on Amiga - the callback runs before the call returns. Useful for Node.js portability.

fs.createReadStream(path, {highWaterMark?})

Returns an EventEmitter emitting 'data' (Buffers) and 'end'. Note: reads the whole file in one sweep - highWaterMark only chooses chunk size for the emitted data events.

fs.createWriteStream(path)

EventEmitter with write(chunk), end(), 'finish' event, bytesWritten.

RAM: is your friend

The Amiga RAM disk (RAM:) is blazing fast and perfect for temp files, caches, and small databases. Use it freely for anything that doesn't need to survive a reboot.

http - HTTP client & server

var http = require('http');

HTTP/1.0 client and server over bsdsocket.library. HTTPS requires AmiSSL (see below).

Client

http.get(url) → {statusCode, body, headers}

Synchronous GET. Follows up to 5 redirects automatically (301/302/303/307/308). Body is always a string.

http.post(url, body, contentType?) → {statusCode, body, headers}

Synchronous POST. body is a string or Buffer. Default content type: application/x-www-form-urlencoded.

http.request(urlOrOptions) → {statusCode, body, headers}

Generic request. options may be {url, method, body, headers}.

Server

http.createServer(handler) → server

Create a server. handler(req, res) is called for each request.

server.listen(port, callback?)

Start accepting connections. Handles up to 8 concurrent clients (non-blocking via WaitSelect). Stops on Ctrl+C.

In the handler:

HTTPS / AmiSSL

http.hasSSL() / http.sslAvailable()

Returns true if HTTPS is available at runtime. Requires NodeAmiga built with -DENABLE_AMISSL AND AmiSSL 5+ installed on the system.

10-second socket timeout

Both send and receive have a 10-second timeout. Slow or unresponsive servers will fail with a connection error. There's currently no way to override this from JavaScript.

net - raw TCP

Currently exposes the same get/request/createServer as http. For a dedicated low-level TCP API, use the FFI module to call bsdsocket.library directly.

buffer

var { Buffer } = require('buffer');  // also available as global

Same Buffer as described in Global API → Buffer. The module export additionally provides Buffer as a named property for Node.js portability.

os - system info

var os = require('os');
os.platform() → "amigaos"
os.arch() → "m68k"
os.type() → "AmigaOS"
os.version() → "39.x" / "45.x" / etc.

Kickstart version from ExecBase->lib_Version.

os.release() → same as version()
os.hostname() → $HOSTNAME or "amiga"
os.tmpdir() → "T:"
os.homedir() → "SYS:"
os.freemem() → bytes

Available memory, from AvailMem(MEMF_ANY).

os.totalmem() → bytes

Returns the same value as freemem() - AmigaOS 2.x has no API for the total installed memory.

os.cpus() → array

Returns a single-element array [{ model, speed }]. Model comes from SysBase->AttnFlags (68000/68010/68020/68030/68040/68060). Speed is always 0 (no portable way to read accelerator clock).

os.uptime() → 0

Not implemented; AmigaOS has no portable uptime API.

os.endianness() → "BE"
os.userInfo() → {username, uid, gid, homedir, shell}

Hardcoded: {username:"amiga", uid:-1, gid:-1, homedir:"SYS:", shell:""}.

os.networkInterfaces() → {}

Empty - TCP/IP stacks differ; NodeAmiga doesn't enumerate them.

Constants: os.EOL = "\n", os.devNull = "NIL:", os.constants.signals.SIGINT=2, os.constants.signals.SIGTERM=15.

path - path manipulation

var path = require('path');

Uses AmigaOS conventions: absolute paths contain : (e.g. SYS:foo/bar), relative don't.

path.basename(p, ext?)

Last segment. Optional ext strips trailing extension.

path.dirname(p)

Everything except the last segment.

path.extname(p)

The .ext part (or empty string).

path.join(...parts)

Concatenate with /. Any part containing : resets the accumulator.

path.resolve(...parts)

Like join but always produces an absolute-looking result.

path.normalize(p)

Collapse .. and ..

path.isAbsolute(p)

Returns true if p contains a colon (Amiga absolute).

path.parse(p) → {root, dir, base, ext, name}
path.format(pathObj)
path.relative(from, to)

Constants: path.sep = '/', path.delimiter = ';'.

url - URL parsing

var url = require('url');

Legacy API

url.parse(str, parseQueryString?)

Returns {protocol, slashes, auth, hostname, port, host, pathname, search, query, hash, path, href}. With parseQueryString=true, query is a parsed object (via querystring.parse).

url.format(urlObj)

Rebuilds a URL string from a parse result.

url.resolve(from, to)

Resolve a relative URL against a base.

WHATWG URL

new URL(input, base?)

Modern URL class. .protocol, .hostname, .port, .pathname, .search, .hash, .host, .origin, .href, .username, .password, .searchParams, .toString(), .toJSON().

Does NOT throw on invalid input - it parses permissively and returns a partial object (spec-divergent).

new URLSearchParams(init?)

append, delete, get, getAll, has, set, sort, toString, forEach, keys, values, entries, .size. Note: keys/values/entries return arrays, not iterators.

querystring

var qs = require('querystring');
qs.parse(str, sep='&', eq='=', {maxKeys?}) → object
qs.stringify(obj, sep='&', eq='=') → string
qs.escape(str) / qs.unescape(str)

Standard Node.js querystring semantics: repeated keys become arrays on parse; arrays produce multiple key=value pairs on stringify.

events - EventEmitter

var EventEmitter = require('events');
var bus = new EventEmitter();
bus.on('ready', () => console.log('go!'));
bus.emit('ready');

Methods: on(event, fn), off(event, fn), once(event, fn), emit(event, ...args), removeListener, removeAllListeners(event?), listenerCount(event), listeners(event), setMaxListeners(n), eventNames(), prependListener, prependOnceListener. Aliases: addListener = on.

Emit passes up to 5 args

emit(event, a, b, c, d, e) passes the first five arguments to each listener. Beyond that, arguments are discarded. (Node.js has no such limit.)

stream

var stream = require('stream');

Pure-JS implementations of Readable, Writable, Duplex, Transform, PassThrough, plus helpers pipeline(...streams, cb) and finished(stream, cb).

var r = new stream.Readable({ read() {} });
r.on('data', chunk => console.log(chunk));
r.push('hello');
r.push(null); // end

Supports pipe(dest), backpressure, highWaterMark (default 16), cork/uncork on Writable.

util

var util = require('util');
util.format(fmt, ...args)

printf-style formatter. Supports %s, %d, %i, %f, %j (JSON), %o/%O (inspect), %%.

util.inspect(obj, {depth?, showHidden?})

Pretty-print an object for debugging. Default depth 2. Truncates arrays at 100 items, objects at 50 keys.

util.inherits(ctor, superCtor)

Classic pre-ES6 inheritance helper.

util.promisify(callbackFn) → Promise-returning fn

Converts a Node-style (err, result) → callback function.

Legacy type predicates: util.isArray/isFunction/isString/isNumber/isBoolean/isNull/isUndefined/isNullOrUndefined/isObject/isPrimitive.

Also: util.noop, util.debuglog(section) (currently returns a no-op).

assert

var assert = require('assert');
assert(x > 0, 'x must be positive');

Methods: assert.ok, assert.equal/notEqual, strictEqual/notStrictEqual, deepEqual/notDeepEqual, deepStrictEqual/notDeepStrictEqual, throws(fn, expected?, msg?), doesNotThrow, fail(msg), ifError(err), match(str, re, msg?), doesNotMatch, rejects(p), doesNotReject(p).

The callable module itself behaves as assert.ok.

deepEqual is strict

assert.deepEqual currently uses the same comparator as deepStrictEqual. It won't coerce 1 == "1" to true. Use assert.equal for loose equality.

zlib

var zlib = require('zlib');
zlib.deflateSync / inflateSync / deflateRawSync / inflateRawSync / gzipSync / gunzipSync

Compress and decompress data. Input and output are strings (cast bytes through Buffer.from/toString if you need Buffers).

zlib.crc32(data) / zlib.adler32(data) → number

deflate does not actually compress

The encoder currently emits "stored" (uncompressed) DEFLATE blocks - the output is larger than the input. The decoder handles fully compressed streams correctly, so this module is useful for reading gzip/deflate data but not for producing it compactly.

crypto

var crypto = require('crypto');
var hash = crypto.createHash('sha256').update('hello').digest('hex');
crypto.createHash(algo) → hasher

Supported: 'md5' (RFC 1321), 'sha256' (FIPS 180-4). Default if omitted: sha256.

The hasher has update(data) (returns this, chainable) and digest(encoding?). Only 'hex' encoding is supported - the encoding argument is accepted but ignored for other values.

No HMAC, no ciphers - plain hash functions only.

readline

var readline = require('readline');
var rl = readline.createInterface({ prompt: '> ' });
rl.question('Name? ', name => { console.log('Hi '+name); rl.close(); });

Interface methods: question(prompt, cb), prompt(), setPrompt(str), close(), on('line', fn), on('close', fn).

Read a file line by line using input: fs.createReadStream(path):

var fs = require('fs');
var readline = require('readline');
var rl = readline.createInterface({ input: fs.createReadStream('data.txt') });
rl.on('line', line => console.log(line));
rl.on('close', () => console.log('done'));

Lines are emitted synchronously as soon as on('line', ...) is attached; trailing CR/LF is stripped. The 'close' event fires after the last line (or immediately if attached after draining). Max line length is 2047 chars.

No line-editing (cursor keys, history) in this module - those work in the REPL via process.stdin at a lower level. Use it for simple input prompts.

dns

var dns = require('dns');
dns.lookup(hostname, callback)

callback(err, address). Resolves a single IPv4 address.

dns.resolve(hostname, callback) / dns.resolve4(hostname, callback)

Returns an array of IPv4 addresses via callback.

dns.resolveSync(hostname) → [address, ...]

Sync version. Returns empty array if no records.

Under the hood, all of these call inet_addr/gethostbyname; the async signatures are for Node.js portability - the resolution is synchronous, the callback fires immediately.

timers (promise-based)

var timers = require('timers/promises');  // or just 'timers'
await timers.setTimeout(500, 'done');
timers.setTimeout(delay, value?)

Returns a Promise that resolves to value after delay ms.

timers.setImmediate(value?)

Alias for setTimeout(0, value).

timers.setInterval(delay)

Returns an async-iterable-like with next(), cancel(), and a then() for one-shot waiting.

string_decoder

var { StringDecoder } = require('string_decoder');

Converts Buffers to strings while handling multi-byte UTF-8 boundaries. Useful when reading a stream chunk by chunk.

new StringDecoder(encoding?) - default 'utf8'. Methods: write(buf), end(buf?). Incomplete trailing bytes are buffered internally and surfaced as a replacement character \uFFFD on end() if they never complete.

punycode

var punycode = require('punycode');
punycode.toASCII('münchen.de');   // 'xn--mnchen-3ya.de'

RFC 3492 IDN encoder/decoder: encode, decode, toASCII, toUnicode, plus ucs2.decode/ucs2.encode.

console (module), constants

var c = require('console');       // same as global console
var K = require('constants');     // POSIX errno & signal names

require('console') simply re-exports the global console object.

require('constants') exposes POSIX-ish numeric codes: signals (SIGTERM=15, ...), errno (ENOENT=2, ...), fs constants (O_RDONLY=0, O_CREAT=64, ...). Values follow BSD conventions, not Amiga IoErr codes - use them for code portability, not for comparing against actual AmigaDOS errors.

iff - IFF/ILBM/8SVX parser

var iff = require('iff');
var img = iff.parseILBM(iff.parseFile('RAM:pic.iff'));

Pure-JS parser for Amiga's native Interchange File Format. Handles ILBM images and 8SVX audio in particular.

iff.parseFile(path) / iff.parse(buffer)

Returns a tree: {type, subType, length, chunks: [...]}. Each chunk has id, length, offset, data (array of bytes), and optionally nested chunks for FORM/LIST/CAT.

iff.findChunk(tree, id) / iff.findChunks(tree, id)

Depth-first chunk search.

iff.parseBMHD(data) → {width, height, numPlanes, compression, ...}

Parse an ILBM bitmap header.

iff.parseCMAP(data) → [{r,g,b}, ...]

Parse a color palette.

iff.parseILBM(tree) → {width, height, numPlanes, colors, pixels, bmhd}

Decode an ILBM all the way to chunky pixel indices. Handles ByteRun1 RLE decompression. Slow in pure JS for large images.

iff.parse8SVX(tree) → {samplesPerSec, numOctaves, name, samples, length, ...}
iff.unpackByteRun1(data, unpackedSize)

Low-level access to the RLE decompression routine.

child_process

var cp = require('child_process');
var out = cp.execSync('version');
cp.execSync(command) → string

Runs an AmigaOS command via SystemTagList, captures stdout via T:nodeamiga_exec_tmp, returns as a string.

cp.exec(command, callback?)

Same as execSync with callback(null, stdout, stderr). stderr is always an empty string - capturing it reliably on AmigaOS is non-trivial.

gui - GadTools widget toolkit

var gui = require('gui');
var win = gui.createWindow({ title: 'Demo', width: 300, height: 150,
    gadgets: [{ kind: 'button', id: 1, label: 'Click me',
                left: 10, top: 10, width: 100, height: 14 }]});
while (true) {
    var evt = gui.waitEvent(win);
    if (evt.type === 'close') break;
    if (evt.type === 'gadgetup' && evt.id === 1) console.log('clicked');
}
gui.closeWindow(win);

High-level GUI using gadtools.library. Handles layout, resizing, events, menus.

Windows

gui.createWindow({title, width, height, left, top, resizable, minWidth, minHeight, font, gadgets})

Create a window. gadgets is an array - see below. Returns a window handle (opaque object).

font may be {name, size}, e.g. {name:'topaz.font', size:8}, to force a fixed-width font (useful for columnar listviews).

gui.closeWindow(win)
gui.waitEvent(win) → event

Blocks until an event arrives. Returns {type, id?, code?, x?, y?, key?, width?, height?}.

gui.pollEvent(win) → event | null

Non-blocking variant.

gui.setTitle(win, title)
gui.setMenu(win, menus)

Install a menu bar. Each entry: {title, items: [{label, id, key?}]}. Use '---' for a separator.

gui.setDisabled(win, gadgetId, flag)

Enable/disable a gadget.

gui.get(win, gadgetId) / gui.set(win, gadgetId, value)

Read/write the value of a gadget (string, number, checkbox state, listview items, etc.).

Requesters

gui.fileRequest({title, pattern?, drawer?, file?, save?}) → {path, file, drawer} | null

ASL file requester.

gui.fontRequest({title}) → {name, size, style} | null
gui.screenInfo() → {fontName, fontWidth, fontHeight, screenWidth, screenHeight, borderLeft, borderRight, borderTop, borderBottom}

Gadget kinds

Pass these in gadgets: array when creating a window. Each has id, left, top, width, height (pixels or 'NN%' strings for layouts relative to the window; negative values anchor from the opposite edge).

kindPurposeEvents
buttonPush button with labelgadgetup
stringText entry, value initialgadgetup on Enter
textRead-only text, value-
integerNumeric entry, valuegadgetup
checkboxBoolean, value: 0/1gadgetup with code=0/1
cycleDropdown, items: [...], value indexgadgetup with code=index
mxRadio-button group, items, value indexgadgetdown with code=index
sliderHorizontal slider, min, max, valuegadgetup/mousemove
scrollerVertical scroller, max, value, visiblegadgetup with code=top
listviewScrollable list, items: [...], value selected indexgadgetup with code=index
numberRead-only numeric display, value-
areaClickable bevel box (custom drawing)gadgetdown/gadgetup/mousemove with evt.x, evt.y

Set flex: true on a gadget to make it expand vertically to fill available space (for a listview that grows with the window).

Drawing (gui.gfx)

For custom drawing on a gui window's RastPort:

gui.gfx.setColor, setBColor, setDrawMode (0=JAM1, 1=JAM2, 2=COMPLEMENT/XOR, 4=INVERSVID), moveTo, lineTo, drawLine, drawRect, fillRect, drawCircle, fillCircle, drawEllipse, drawText, setPixel, getPixel, clear, waitTOF (vsync), innerSize, setFont.

intuition - low-level windows

var ui = require('intuition');
var win = ui.openWindow({ title: 'Raw', width: 320, height: 200 });
while (true) {
    var evt = win.waitEvent();
    if (evt.type === 'close') break;
}
win.close();

Low-level wrapper over intuition.library for plain Intuition windows - no GadTools widgets, just a blank surface you can draw on. Use this when gui is too heavy or when you want total control (games, demos, custom toolkits).

ui.openWindow({title, width, height, left, top}) → win

width/height are the inner (drawable) dimensions. Returns a window handle with methods close(), waitEvent(), pollEvent(), plus properties width, height, innerWidth, innerHeight.

Events: close, mousedown, mouseup, mousemove, keypress (with code and key), resize (when window is resized), refresh.

ui.screenInfo() → {width, height, depth}

Size of the active public screen (usually Workbench).

ui.alert(text) / ui.confirm(text) → boolean

Simple modal requesters via EasyRequestArgs.

Graphics (ui.gfx)

Same API surface as gui.gfx. Examples: ui.gfx.setColor(win, 1), ui.gfx.drawLine(win, 10,10, 100,50), etc.

clipboard

var clip = require('clipboard');
clip.write('hello');
var text = clip.read();
clip.clear();

Read/write the standard AmigaOS clipboard unit 0. Uses iffparse.library to read/write FTXT/CHRS chunks - compatible with all standard Amiga text editors.

clip.read() → string

Returns "" if the clipboard is empty or contains no text. Reads up to 1 MB per chunk.

clip.write(text) → boolean

Returns true on success.

clip.clear()

Empties the clipboard (writes an empty FTXT).

arexx

var arexx = require('arexx');
var r = arexx.send('EDITOR', 'REDRAW');
console.log(r.rc, r.result);

Communicate with ARexx-aware AmigaOS applications via message ports.

arexx.send(portName, command) → {rc, result}

Send a command to a port and wait for the reply. rc is the ARexx return code (0 on success, non-zero on error). result is the string result, or an error description if the port wasn't found.

arexx.createPort(name) → port

Create a named port that other applications can send commands to.

arexx.closePort(port)
arexx.getMsg(port) → {command, _msg} | null

Non-blocking; returns null if no message is waiting.

arexx.waitMsg(port) → {command, _msg}

Blocks until a message arrives.

arexx.reply(msg, rc, result)

Reply to a received message.

arexx.listPorts() → [name, ...]

Returns the names of all public ports in the system.

amiga - FFI (call any library)

var amiga = require('amiga');
var intBase = amiga.openLibrary('intuition.library', 37);
var version = amiga.peek16(intBase, 20);  // lib_Version
amiga.closeLibrary(intBase);

Escape hatch for calling AmigaOS libraries directly from JavaScript. Useful when a feature you need isn't wrapped in a higher-level NodeAmiga module.

Libraries

amiga.openLibrary(name, version?) → base

Returns the library base pointer as a number, or 0 on failure.

amiga.closeLibrary(base)
amiga.execBase() → SysBase pointer

Memory

amiga.allocMem(size, flags?) → address

Default flags: MEMF_PUBLIC|MEMF_CLEAR. Allocations are tracked and freed automatically on exit if you forget.

amiga.freeMem(address, size)
amiga.peek8/16/32(address, offset?)

Read an unsigned byte/word/long.

amiga.poke8/16/32(address, offset, value)
amiga.peekString(address, offset?, maxLen?)

Read a null-terminated string (default max 4096 bytes).

amiga.pokeString(address, offset, str)

No bounds check! You are responsible for making sure str fits. Allocate enough with allocMem first.

amiga.makeString(str) → address

Allocate an AmigaOS-owned buffer and copy the string in. Tracked for auto-cleanup.

amiga.makeTags([tag1, val1, tag2, val2, ...]) → address

Allocate a TagItem array. Terminate your list with TAG_DONE (value 0) or the last pair; makeTags handles the terminator automatically.

Function calls

amiga.call(base, offset, regs) → d0

Call a library function at the given LVO offset (negative, e.g. -216 for exec AvailMem). regs is an object like {d0: ..., d1: ..., a0: ...}. Returns the value in d0 as a number.

Uses an assembly trampoline (ffi_asm.s) to load d0-d7 and a0-a5 from your object, sets a6 to base automatically, then calls the function.

// example: exec AvailMem(MEMF_CHIP) via FFI
var execLib = amiga.execBase();
var chip = amiga.call(execLib, -216, { d1: 0x02 });  // MEMF_CHIP
console.log('Chip free:', chip, 'bytes');

FFI is unsafe - by design

This module bypasses all type checking. Incorrect register values or out-of-bounds writes will crash your Amiga. Use it carefully and test on real hardware (or an emulator snapshot) before shipping.

locale - localised catalogs (locale.library v38+)

var locale = require('locale');
var cat = locale.openCatalog('myapp.catalog');
console.log(locale.getString(cat, 1, 'Hello!'));
locale.closeCatalog(cat);
locale.close();

Thin wrapper around AmigaOS locale.library v38+ over the amiga FFI bridge. Loads .catalog files (built with FlexCat or CatComp) and looks up localised strings by numeric ID. The OS resolves catalog paths through PROGDIR:catalogs/<language>/ and LOCALE:catalogs/<language>/ - the same way a native app's translations work. The user's preferred language comes from Locale Preferences (also visible to scripts as process.env.Language).

locale.available() → boolean

Opens locale.library lazily on first call. Returns true if the library opened and the system default locale was acquired. False on Kickstart < 38 or low-memory failure.

locale.openCatalog(name, version?) → handle

Returns an opaque Catalog handle as a number, or 0 if the catalog file isn't installed for the current language. version is reserved for a future OC_Version tag.

locale.closeCatalog(handle)
locale.getString(handle, id, defaultStr) → string

Always returns a string. If the catalog handle is 0 or the ID isn't in the catalog, returns defaultStr. Safe to call without a successful openCatalog - your app will just show the fallbacks. See examples/locale_demo.js for the step-by-step pattern.

locale.close()

Closes the default locale and the library. Optional - done at process exit anyway, but useful for long-running scripts.