-
-
Notifications
You must be signed in to change notification settings - Fork 5
Standard Library
The DScript.Extras package provides a JavaScript-compatible standard library. Register it with:
new EngineFunctionLoader().RegisterFunctions(engine);
// or with permission restrictions:
new EngineFunctionLoader().RegisterFunctions(engine, EnginePermissions.FileSystem | EnginePermissions.Network);See Permissions for the full list of flags.
Built into the engine (no DScript.Extras required).
// Constructor
var p = new Promise(function(resolve, reject) {
resolve(42); // or reject("reason")
});
// Static factory methods
Promise.resolve(42) // already-fulfilled Promise
Promise.reject("oops") // already-rejected Promise
Promise.all([p1, p2]) // resolves when all resolve; rejects on first rejection
Promise.allSettled([p1, p2]) // always resolves with [{status, value/reason}, …]
Promise.race([p1, p2]) // settles with whichever input settles first
Promise.any([p1, p2]) // resolves with first fulfillment; rejects (AggregateError) if all reject
// Instance methods
p.then(function(v) { /* fulfilled */ })
.catch(function(e) { /* rejected */ })
.finally(function() { /* runs on both paths; propagates original value/reason */ });Promise.any rejects with an AggregateError object when all inputs reject:
Promise.any([Promise.reject("a"), Promise.reject("b")])
.catch(function(e) {
e.name; // "AggregateError"
e.message; // "All promises were rejected"
e.errors; // ["a", "b"]
});Built into the engine (no DScript.Extras required).
// Create a symbol (each call returns a unique value)
var s = Symbol(); // anonymous symbol
var s2 = Symbol('label'); // symbol with description
// Symbols are unique even with the same description
Symbol('x') === Symbol('x') // false
// typeof
typeof Symbol() // "symbol"
// Global symbol registry
var s1 = Symbol.for('app.id'); // creates or retrieves from registry
var s2 = Symbol.for('app.id'); // same symbol
s1 === s2 // true
Symbol.keyFor(s1) // "app.id"
Symbol.keyFor(Symbol('x')) // undefined (not in registry)
// Symbol as a property key
var id = Symbol('id');
var obj = {};
obj[id] = 42;
obj[id]; // 42
// Well-known symbols
Symbol.iterator // used by for...of — define [Symbol.iterator]() to make objects iterable
Symbol.hasInstance // customise instanceof
Symbol.toPrimitive // customise type coercion
Symbol.toStringTag // customise Object.prototype.toString tagvar range = {};
range[Symbol.iterator] = function() {
var i = 0;
return {
next: function() {
if (i < 3) return { value: i++, done: false };
return { value: undefined, done: true };
}
};
};
for (var x of range) { /* x = 0, 1, 2 */ }var Even = {};
Even[Symbol.hasInstance] = function(n) { return n % 2 === 0; };
4 instanceof Even // true
3 instanceof Even // false| Function | Description |
|---|---|
eval(str) |
Parse and execute a string as DScript code |
exec(str) |
Execute a statement string (alias of eval for statements) |
trace(val) |
Print a debug representation of any value |
parseInt(str, radix?) |
Parse an integer; radix defaults to 10 |
parseFloat(str) |
Parse a floating-point number |
isNaN(val) |
True if val is NaN |
isFinite(val) |
True if val is a finite number |
encodeURIComponent(str) |
Percent-encode a URI component |
decodeURIComponent(str) |
Decode a percent-encoded URI component |
encodeURI(str) |
Encode a full URI (preserves : / ? # …) |
decodeURI(str) |
Decode a full URI |
btoa(str) |
Base-64 encode a string |
atob(str) |
Base-64 decode a string |
charToInt(ch) |
Return the Unicode code point of the first character |
structuredClone(val) |
Deep-clone any value |
queueMicrotask(fn) |
Queue fn on the microtask queue |
console.log("hello");
console.error("oops");
console.warn("careful");
console.info("fyi");
console.clear();
console.time("label");
// ... work ...
console.timeEnd("label"); // prints elapsed ms
console.timeLog("label"); // prints elapsed ms without stoppingRedirecting output from C# — set static delegates before registering:
ConsoleFunctionProvider.SetOutput(
stdout: msg => myLog.Info(msg),
stderr: msg => myLog.Error(msg)
);Math.abs(-5) // 5
Math.ceil(1.2) // 2
Math.floor(1.9) // 1
Math.round(1.5) // 2
Math.trunc(1.9) // 1
Math.sign(-3) // -1
Math.min(1, 2, 3) // 1
Math.max(1, 2, 3) // 3
Math.pow(2, 10) // 1024
Math.sqrt(16) // 4
Math.cbrt(27) // 3
Math.hypot(3, 4) // 5
Math.random() // float in [0,1)
Math.log(Math.E) // 1
Math.log2(8) // 3
Math.log10(1000) // 3
Math.sin(x) Math.cos(x) Math.tan(x)
Math.asin(x) Math.acos(x) Math.atan(x) Math.atan2(y, x)
Math.sinh(x) Math.cosh(x) Math.tanh(x)
Math.exp(x)
Math.clz32(x) // count leading zeros in 32-bit int
Math.fround(x) // nearest 32-bit float
Math.imul(a,b) // 32-bit integer multiplyConstants: Math.PI, Math.E, Math.SQRT2, Math.SQRT1_2, Math.LN2, Math.LN10, Math.LOG2E, Math.LOG10E
Instance methods are available on all string values.
var s = "Hello, World!";
s.length // 13
s.charAt(0) // "H"
s.charCodeAt(0) // 72
s.codePointAt(0) // 72
s.indexOf("o") // 4
s.lastIndexOf("o") // 8
s.includes("World") // true
s.startsWith("Hello") // true
s.endsWith("!") // true
s.slice(7, 12) // "World"
s.substring(7, 12) // "World"
s.toUpperCase() // "HELLO, WORLD!"
s.toLowerCase() // "hello, world!"
s.trim() // strips whitespace
s.trimStart() // strips leading whitespace
s.trimEnd() // strips trailing whitespace
s.split(", ") // ["Hello", "World!"]
s.replace("World", "DScript") // "Hello, DScript!"
s.replaceAll("l", "L") // "HeLLo, WorLd!"
s.repeat(2) // "Hello, World!Hello, World!"
s.padStart(15, "*") // "**Hello, World!"
s.padEnd(15, "-") // "Hello, World!--"
s.concat(" ", "More") // "Hello, World! More"
s.match(/\w+/g) // ["Hello", "World"]
s.matchAll(/(\w+)/g) // iterator of match arrays
s.normalize("NFC") // Unicode normalization
s.at(-1) // "!"
String.fromCharCode(72, 101, 108) // "Hel"
String.fromCodePoint(128512) // emoji 😀Instance methods are available on all array values.
var a = [3, 1, 4, 1, 5];
// Mutating
a.push(9) // append; returns new length
a.pop() // remove last; returns element
a.shift() // remove first; returns element
a.unshift(0) // prepend; returns new length
a.splice(1, 2) // remove 2 at index 1; returns removed
a.splice(1, 0, 99) // insert 99 at index 1
a.reverse() // in-place reverse
a.sort() // in-place sort (lexicographic by default)
a.sort((a,b) => a - b) // with comparator
a.fill(0, 2, 4) // fill indices 2..3 with 0
a.copyWithin(0, 2) // copy from index 2 to start
// Non-mutating
a.slice(1, 3) // copy of indices 1..2
a.concat([6, 7]) // new array
a.join(", ") // "3, 1, 4, 1, 5"
a.indexOf(1) // 1
a.lastIndexOf(1) // 3
a.includes(4) // true
a.find(x => x > 3) // 4
a.findIndex(x => x > 3) // 2
a.every(x => x > 0) // true
a.some(x => x > 4) // true
a.flat() // flatten one level
a.flat(Infinity) // flatten all levels
a.at(-1) // last element
// Higher-order
a.map(x => x * 2)
a.filter(x => x > 2)
a.forEach(x => console.log(x))
a.reduce((acc, x) => acc + x, 0)
a.reduceRight((acc, x) => acc + x, 0)
a.flatMap(x => [x, x * 2])
// Immutable copies (ES2023)
a.toSorted() // sorted copy
a.toReversed() // reversed copy
a.toSpliced(1, 1) // splice copy
a.with(2, 99) // copy with index 2 replaced
// Iteration
a.keys() // index iterator
a.values() // value iterator
a.entries() // [index, value] iterator
// Static
Array.isArray(a) // true
Array.from("abc") // ["a","b","c"]
Array.of(1, 2, 3) // [1,2,3]var obj = { a: 1, b: 2, c: 3 };
Object.keys(obj) // ["a","b","c"]
Object.values(obj) // [1,2,3]
Object.entries(obj) // [["a",1],["b",2],["c",3]]
Object.fromEntries([["a",1]]) // { a: 1 }
Object.assign({}, obj, { d: 4 }) // shallow merge
Object.create(proto) // new object with prototype
Object.freeze(obj) // prevent all modification
Object.isFrozen(obj) // true/false
Object.seal(obj) // prevent add/delete but allow modification
Object.isSealed(obj) // true/false
Object.hasOwn(obj, "a") // true (ES2022)
Object.is(NaN, NaN) // true (handles NaN and -0 correctly)
Object.groupBy(arr, fn) // group array items into an object by key
obj.hasOwnProperty("a") // trueNumber.isInteger(42) // true
Number.isFinite(1/0) // false
Number.isNaN(NaN) // true
Number.isSafeInteger(2**53) // false
Number.parseInt("42px") // 42
Number.parseFloat("3.14x") // 3.14
(3.14159).toFixed(2) // "3.14"
(3.14159).toPrecision(4) // "3.142"
(255).toString(16) // "ff"
(1234).toExponential(2) // "1.23e+3"Constants: Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_SAFE_INTEGER, Number.MIN_SAFE_INTEGER, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NaN, Number.EPSILON
var obj = JSON.parse('{"a":1,"b":[2,3]}');
var json = JSON.stringify(obj); // '{"a":1,"b":[2,3]}'
var pretty = JSON.stringify(obj, null, 2); // indentedvar m = new Map();
m.set("key", 42);
m.get("key"); // 42
m.has("key"); // true
m.delete("key");
m.size; // 0
m.clear();
m.forEach((v, k) => console.log(k, v));
m.keys(); m.values(); m.entries();
// ES2024 groupBy
var grouped = Map.groupBy([1,2,3,4], x => x % 2 === 0 ? "even" : "odd");var s = new Set([1, 2, 3, 2, 1]);
s.size; // 3
s.has(2); // true
s.add(4);
s.delete(1);
s.clear();
s.forEach(v => console.log(v));
s.keys(); s.values(); s.entries();Object-keyed map that does not prevent garbage collection of its keys (in DScript, keys are always live — the weak semantics are preserved structurally).
var wm = new WeakMap();
var key = {};
wm.set(key, "value");
wm.get(key); // "value"
wm.has(key); // true
wm.delete(key); // true| Method | Description |
|---|---|
get(key) |
Returns the value for key, or undefined
|
set(key, val) |
Sets the value; returns the WeakMap |
has(key) |
Returns true if key is present |
delete(key) |
Removes the entry; returns true if found |
Object-keyed set that does not prevent garbage collection of its members.
var ws = new WeakSet();
var obj = {};
ws.add(obj);
ws.has(obj); // true
ws.delete(obj); // true
ws.has(obj); // false| Method | Description |
|---|---|
add(obj) |
Adds the object; returns the WeakSet |
has(obj) |
Returns true if obj is a member |
delete(obj) |
Removes the member; returns true if found |
Holds a weak reference to an object. In DScript's GC-less engine, deref() always returns the original target.
var target = { x: 1 };
var ref = new WeakRef(target);
ref.deref(); // the original target object
ref.deref().x; // 1| Method | Description |
|---|---|
deref() |
Returns the referenced object (always live in DScript) |
var now = new Date();
var d = new Date(2024, 0, 15); // Jan 15 2024
d.getFullYear() // 2024
d.getMonth() // 0 (January)
d.getDate() // 15
d.getDay() // day of week (0=Sun)
d.getHours() d.getMinutes() d.getSeconds()
d.getMilliseconds() d.getTime() // ms since epoch
d.toISOString() // "2024-01-15T00:00:00.000Z"
d.toLocaleDateString()
d.toString()
Date.now() // current timestamp in msvar re = new RegExp("(\\d+)", "g");
re.test("abc123"); // true
re.exec("abc123"); // ["123","123"]
re.source // "(\\d+)"
re.flags // "g"
re.global // true
re.ignoreCase // false
re.multiline // falseBinary data backed by a byte[].
// Creating
var b1 = Buffer.from("hello", "utf8");
var b2 = Buffer.from([72, 101, 108, 108, 111]);
var b3 = Buffer.alloc(10); // zeroed
var b4 = Buffer.allocUnsafe(10); // uninitialized
// Checking
Buffer.isBuffer(b1); // true
// Reading
b1.length; // 5
b1.toString("utf8"); // "hello"
b1.toString("hex"); // "68656c6c6f"
b1.toString("base64"); // "aGVsbG8="
b1.readUInt8(0); // 72
// Modifying
b3.writeUInt8(255, 0);
// Operations
Buffer.concat([b1, b2]);
b1.slice(1, 3);
b1.copy(b3, 0); // copy b1 into b3 starting at offset 0
b1.equals(b2); // falsevar emitter = new EventEmitter();
emitter.on("data", function(chunk) {
console.log("received:", chunk);
});
emitter.once("end", function() {
console.log("stream ended");
});
emitter.emit("data", "hello");
emitter.emit("end");
emitter.listeners("data"); // array of listeners
emitter.listenerCount("data"); // 0 (once fired and removed)
emitter.removeAllListeners("data");
emitter.off("data", myHandler);EventEmitter.defaultMaxListeners (default 10) — a warning is printed when exceeded.
var id = setTimeout(function() {
console.log("fired after 100ms");
}, 100);
clearTimeout(id);
var interval = setInterval(function() {
console.log("tick");
}, 1000);
clearInterval(interval);Timers do not fire automatically — the host must call engine.DrainTimers() periodically:
engine.Run(program);
while (engine.HasPendingTimers)
{
Thread.Sleep(10);
engine.DrainTimers();
}process.platform // "win32", "linux", "darwin", …
process.version // engine version string
process.argv // array of command-line arguments
process.env // object of environment variables
process.getenv("PATH") // read one environment variable
process.cwd() // current working directory
process.exit(0) // exit with code
// Lifecycle hooks
process.on("exit", function(code) {
console.log("exiting with", code);
});
process.on("uncaughtException", function(err) {
console.error("unhandled error:", err.message);
});
process.on("unhandledRejection", function(reason) {
console.error("unhandled rejection:", reason);
});Reading process.env and calling process.exit require the appropriate EnginePermissions flags.
assert(value, "message"); // alias of assert.ok
assert.ok(value, "msg"); // throws if falsy
assert.equal(a, b, "msg"); // == comparison
assert.strictEqual(a, b, "msg"); // === comparison
assert.notEqual(a, b);
assert.notStrictEqual(a, b);
assert.deepEqual(a, b); // recursive value equality
assert.throws(fn, errorMatcher?); // asserts fn throws
assert.doesNotThrow(fn);
assert.fail("forced failure");util.format("%s is %d years old", "Alice", 30);
// "Alice is 30 years old"
// Placeholders: %s (string), %d/%i (int), %f (float), %o (inspect), %j (JSON)
util.inspect({ a: 1, b: [2, 3] });
// "{ a: 1, b: [ 2, 3 ] }"
var readFileAsync = util.promisify(fs.readFile);
readFileAsync("data.txt").then(contents => console.log(contents));
var oldFn = util.deprecate(function() {}, "use newFn instead");
oldFn(); // prints deprecation warning on first callpath.join("a", "b", "c.txt") // "a/b/c.txt" (or "a\b\c.txt" on Windows)
path.resolve(".", "src", "app") // absolute path
path.dirname("/foo/bar/baz.txt") // "/foo/bar"
path.basename("/foo/bar/baz.txt") // "baz.txt"
path.basename("/foo/bar/baz.txt", ".txt") // "baz"
path.extname("baz.txt") // ".txt"
path.isAbsolute("/foo") // true
path.normalize("/foo//bar/../baz") // "/foo/baz"
path.sep // "/" or "\\"os.hostname() // "mypc"
os.platform() // "linux" / "win32" / "darwin"
os.arch() // "x64" / "arm64"
os.homedir() // "/home/user"
os.tmpdir() // "/tmp"
os.totalmem() // total RAM in bytes
os.freemem() // free RAM in bytes
os.cpus() // number of logical CPUs
os.EOL // "\n" or "\r\n"All methods are synchronous. Requires EnginePermissions.FileSystem.
fs.readFileSync("file.txt") // string (UTF-8)
fs.readFileSync("file.bin", "binary") // binary string
fs.writeFileSync("out.txt", "hello")
fs.appendFileSync("log.txt", "line\n")
fs.existsSync("file.txt") // true/false
fs.mkdirSync("new/dir", { recursive: true })
fs.rmdirSync("old/dir")
fs.unlinkSync("file.txt")
fs.readdirSync(".") // array of entry names
fs.renameSync("old.txt", "new.txt")
fs.copyFileSync("src.txt", "dst.txt")
var stat = fs.statSync("file.txt");
stat.size // bytes
stat.isFile() // true/false
stat.isDirectory() // true/false
stat.mtime // last-modified timestampcrypto.randomUUID() // "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
crypto.randomBytes(16) // Buffer of 16 random bytes
crypto.getRandomValues(arr) // fills a script array with random ints
var hash = crypto.createHash("sha256");
hash.update("hello");
hash.digest("hex"); // "2cf24dba…"
hash.digest("base64"); // base64-encoded
hash.digest(); // Buffer
var hmac = crypto.createHmac("sha256", "secret");
hmac.update("message");
hmac.digest("hex");Supported algorithms: sha256, sha512, sha1, md5.
Synchronous HTTP client. Requires EnginePermissions.Network.
var res = fetch("https://api.example.com/data");
// or
var res = fetch("https://api.example.com/data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: "value" })
});
res.ok; // true if status 200–299
res.status; // 200
res.statusText; // "OK"
res.headers; // object of response headers
res.text(); // response body as string
res.json(); // parsed JSON object
res.arrayBuffer(); // BufferHTTP server. Requires EnginePermissions.Network.
var server = http.createServer(function(req, res) {
console.log(req.method, req.url);
req.on("data", function(chunk) { /* request body */ });
req.on("end", function() {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello, World!");
});
server.close(); // stop after one request
});
server.listen(8080, "localhost", function() {
console.log("listening on port 8080");
});server.listen blocks until server.close() is called.
Requires EnginePermissions.ProcessSpawn.
// Synchronous — blocks until done
var out = child_process.execSync("echo hello"); // stdout string
// throws ScriptException on non-zero exit
var result = child_process.spawnSync("node", ["-e", "console.log(1)"]);
result.stdout // string
result.stderr // string
result.status // exit code (int)
result.signal // null or "SIGTERM"
// Async (callback style)
child_process.exec("echo hello", function(err, stdout, stderr) {
if (err) { console.error(err); return; }
console.log(stdout);
});
// Spawn — returns process object
var proc = child_process.spawn("node", ["-e", "console.log('hi')"]);
proc.on("exit", function(code) { console.log("exited:", code); });
proc.on("data", function(chunk) { console.log("stdout:", chunk); });
proc.pid; // process IDvar rl = readline.createInterface({
input: process.stdin, // or any Readable with a TextReader via SetData
output: process.stdout
});
rl.question("What is your name? ", function(answer) {
console.log("Hello,", answer);
rl.close();
});
rl.on("line", function(line) { console.log("got:", line); });
rl.on("close", function() { console.log("closed"); });TCP networking. Requires EnginePermissions.Network.
// TCP server
var server = net.createServer(function(socket) {
var data = socket.read();
socket.write("echo: " + data);
socket.end();
server.close();
});
server.listen(9000, "127.0.0.1", function() {
console.log("listening");
});
server.on("connection", function(socket) { /* alternative handler */ });
// TCP client
var sock = net.createConnection(9000, "127.0.0.1", function() {
console.log("connected");
});
sock.write("hello");
sock.read(); // returns available data (non-blocking)
sock.end();
sock.on("data", function(chunk) { });
sock.on("close", function() { });
sock.on("connect", function() { });
sock.remoteAddress; // "127.0.0.1"
sock.remotePort; // 9000// Readable
var r = new stream.Readable();
r.on("data", function(chunk) { console.log("chunk:", chunk); });
r.on("end", function() { console.log("done"); });
r.push("hello ");
r.push("world");
r.push(null); // signals end of stream
r.read(); // returns and clears the internal buffer
// Writable
var w = new stream.Writable();
w.on("finish", function() { console.log("finished"); });
w.write("foo");
w.end("bar");
w.getBuffer(); // "foobar" — returns accumulated content (non-Node API)
// Pipe
r.pipe(w); // writes all buffered Readable data into Writable then calls end()
// Transform (combined Readable + Writable)
var t = new stream.Transform();
t.on("data", function(chunk) { /* receive transformed output */ });
t.write("input");
t.end();var compressed = zlib.gzipSync("hello world"); // Buffer
var decompressed = zlib.gunzipSync(compressed); // Buffer
decompressed.toString("utf8"); // "hello world"
var deflated = zlib.deflateSync("hello");
var inflated = zlib.inflateSync(deflated);
inflated.toString("utf8"); // "hello"Input can be a Buffer or a plain string (treated as UTF-8). All methods are synchronous.
var u = new URL("https://user:pass@example.com:8080/path?a=1&b=2#frag");
u.href // full URL string
u.protocol // "https:"
u.host // "example.com:8080"
u.hostname // "example.com"
u.port // "8080"
u.pathname // "/path"
u.search // "?a=1&b=2"
u.hash // "#frag"
u.origin // "https://example.com:8080"
u.username // "user"
u.password // "pass"
u.toString() // same as href
u.searchParams.get("a") // "1"
u.searchParams.getAll("a") // ["1"]
u.searchParams.has("b") // true
u.searchParams.set("a", "99")
u.searchParams.append("c", "3")
u.searchParams.delete("b")
u.searchParams.toString() // "a=99&c=3"
// Relative URL
var rel = new URL("/other", "https://example.com");
rel.href; // "https://example.com/other"
// Standalone URLSearchParams
var sp = new URLSearchParams("x=1&y=2");
sp.set("x", "hello");
sp.toString(); // "x=hello&y=2"var enc = new TextEncoder();
enc.encoding; // "utf-8"
var buf = enc.encode("hello 😀"); // Buffer
var dec = new TextDecoder(); // defaults to "utf-8"
dec.decode(buf); // "hello 😀"
var decLatin = new TextDecoder("latin1");
decLatin.encoding; // "latin1"
decLatin.decode(buf); // (Latin-1 decoded string)Supported encodings: utf-8 (default), ascii, latin1 / iso-8859-1 / binary.