npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

novac

v2.4.0

Published

a rewrite version of my package nvlang

Readme

novac Language Reference

novac is a dynamic, expressive scripting language with a JavaScript-style syntax, a rich infix operator set, built-in event system, HTTP primitives, a Nova Classic layer, and a comprehensive standard library. Files use the .nv or .nova extension and are run with the novac CLI.


Table of Contents

  1. Comments
  2. Preprocessor Directives
  3. Literals & Values
  4. Variables & Smart Modifiers
  5. Types
  6. Operators
  7. Strings & F-Strings
  8. Control Flow
  9. Functions
  10. Classes
  11. Pattern Matching
  12. Events
  13. Error Handling
  14. Modules & Namespaces
  15. HTTP & Networking
  16. Nova Classic Features
  17. Built-in Global Objects
  18. Built-in Class Objects
  19. Standard Library (std)
  20. Standard Built-ins (bstd / import_builtin)
  21. Array Methods
  22. Object Methods
  23. String Methods
  24. Type System & Casting
  25. Threads

Comments

// single-line comment
/! also a single-line comment
/* multi-line block comment */

/?/ core.print("executed at lex time")  // executable comment — runs during lexing

Preprocessor Directives

#define NAME value       // substitution macro (NAME replaced everywhere in source)
#define FLAG             // valueless flag (for ifdef/ifndef)
#undef NAME             // remove a macro
#ifdef FLAG             // include following block only if FLAG is defined
#ifndef FLAG            // include following block only if FLAG is NOT defined
#endif                  // close an ifdef/ifndef block
#register operator NAME {"precedence":N, "isUnary":bool}
#inject <json-token>    // inject a raw token into the token stream

Literals & Values

Numbers

| Form | Example | Notes | |------|---------|-------| | Decimal | 42, 3.14 | Standard | | Separator | 1_000_000 | Underscores ignored | | Hex | 0xFF | Case insensitive | | Binary | 0b1010 | Case insensitive | | Custom base | 0r16FF | 0r<base><digits>, base 2–36 | | Holland/scale | 0hbillion, 0hmega | Named SI / large-number literals | | BigInt | 42n | Suffix n | | Scientific | 1e6, 2.5E-3 | Standard scientific | | Short suffix | 10k, 2m, 1b, 4t | k=1e3, m=1e6, b=1e9, t=1e12 |

Holland scale literals0h prefix:

| Name | Value | Name | Value | |------|-------|------|-------| | kilo | 1e3 | milli | 1e-3 | | mega | 1e6 | micro | 1e-6 | | giga | 1e9 | nano | 1e-9 | | tera | 1e12 | pico | 1e-12 | | million | 1e6 | femto | 1e-15 | | billion | 1e9 | atto | 1e-18 | | trillion | 1e12 | googol | 1e100 | | quadrillion | 1e15 | | | | quintillion | 1e18 | | |

Special Literals

| Token | Value | |-------|-------| | true | boolean true | | false | boolean false | | null | null | | nstr | "" (empty string) | | nfunc | A function that returns null |

URL Literals

Tokens beginning with a recognized scheme are URL literals:

let url = https://api.example.com/users
let local = localhost:3000
// Recognized: http, https, ftp, ws, wss, content, localhost:<port>

Arrays

let arr = [1, 2, 3]
let spread = [...arr, 4, 5]

Objects

let obj = { x: 1, 'y': 2 }
let method = { greet() { give "hi"; } }
let overload = { 
  [op]: { 
    unary: { '~': () => 17 }
    binary: { '+': (a) => a*10  }
  }
}
core.print(~obj + obj); // ~obj becomes 17, now 17 + obj becomes 17*10 which is 170.
let computed = { ["key" + idx]: value }
let withSpread = { ...obj, z: 3 }
let getset = {
  get prop() { give this._val; },
  set prop(v) { this._val = v; }
}
let presence = { active? true }   // boolean-constrained property: must be true or false

Variables & Smart Modifiers

var x = 10      // mutable, function-scoped
let y = 20      // mutable, block-scoped
const Z = 30    // immutable binding/
// special __...__ identifiers:
let __my cool variable++-__ = 19; // when novac sees __, it collects everything until __ as the var name and gives an identifier token type, so you can even name a variable __26__ and the token with be { type: 'IDENTIFIER', value: '26' }

Destructuring

let { a, b } = obj
let { a: renamed, b: other = "default" } = obj
let [first, second] = arr
let [head, ...tail] = arr

Smart Variable Modifiers

Placed between the variable name and =:

| Modifier | Effect | |----------|--------| | frozen | Throws on any reassignment after first set | | lazy | Expression evaluated only on first read | | tracked | console.logs every assignment change | | nonull | Throws if assigned null or undefined | | once | Can only be set once; further assigns silently ignored | | setter <fn> | Calls fn(newVal, oldVal) on every assignment; if fn returns non-undefined, that becomes the stored value | | getter <fn> | Calls fn() on every read | | as fnum [min, max] | Clamps to float range on every assign | | as fint [min, max] | Clamps to integer range (Math.trunc) on every assign |

let x frozen = 5
let y lazy = computeHeavy()
let z tracked = 0
let n nonull = getValue()
let once_val once = initialize()
let watched setter mySetFn = 0
let computed getter myGetFn = 0
let clamped as fnum [0.0, 1.0] = 0.5
let bounded as fint [0, 255] = 128

Links and $-notations

let obj = { test: { example: 29 } };
let link2example = link(obj.test.example); // link is not a function, its a special keyword function
link2example = 3; // example is also set
obj.test.example; // 3
obj.test.example = 2;
link2example; // 2
// you can also directly set a link call:
link(obj.test.example) = 1; // works
// nest links:
link(link2example) = 3; // now obj.test.example is also 3
// $ notation:
// when used as a normal call:
$29; // noop, just 29
${a: 6 + 3}; // noop again, just the object
// real magic in $ assingments:
$"hi" = 29; // sets variable hi
$26 = 1; // set variable 26, even tho you can't just read from a var directly called 26, you can still use __26__ to read (novac trims __...__ and just leaves an ident with value ...)

Type Annotation

let count: int = 0
let name: string = "Nova"
func add(a: int, b: int): int => { give a + b }

Pointer Declaration & Dereference

var *ptr = someValue
let result = *ptr     // dereference
*ptr = newValue       // assign through pointer

Types

Type Alias

type ID = string
type Status = "active" | "inactive" | "pending"
type Point = { x: int, y: int }
type Result<T> = T | null

Struct

struct Vec2 {
  x: int = 0,
  y: int = 0
}
let v = Vec2({ x: 3, y: 4 })
v.x   // 3

Interface

interface Serializable {
  serialize(): string
  deserialize(data: string): bool
  name?: string               // optional member
}
interface Extended extends Serializable, Printable { }

Enum

enum Status { Active, Inactive, Pending }
enum Shape {
  Circle(int),
  Rect(int, int),
  Point = 0
}
let s = Status.Active
s.__variant__         // "Active"
s.__enum_type__       // "Status"

Trait & Impl

trait Drawable {
  draw(ctx) { }
}
impl Drawable of Circle {
  draw(ctx) { ctx.circle(this.x, this.y, this.r); }
}
// Also: impl Drawable for Circle { }

Type Expressions

string | null               // union
int & Serializable          // intersection
string[]                    // array type
{ name: string, age?: int } // inline shape type
Map<string, int>            // generic

Operators

Arithmetic

| Op | Meaning | |----|---------| | + - * / % | Standard arithmetic | | ** | Exponentiation (right-associative) | | ++ -- | Increment / decrement (prefix or postfix) |

Assignment

| Op | Meaning | |----|---------| | = | Assign | | += -= *= /= %= **= | Compound assign | | &&= | Logical-AND assign | | \|\|= | Assign if current is null/undefined | | ??= | Nullish-coalesce assign |

Comparison

| Op | Meaning | |----|---------| | == != | Loose equality | | === !== | Strict equality | | < > <= >= | Relational | | is | Strict equality alias | | isnt | Non-identity alias | | equals | === alias | | equals_ignore | Case-insensitive string equality | | cmp | localeCompare — returns -1/0/1 |

Logical

| Op | Meaning | |----|---------| | && / and | Logical AND | | \|\| / or | Logical OR | | ! / not | Logical NOT (unary) | | ?? | Nullish coalesce | | xor | Exclusive OR | | nand | NOT AND | | nor | NOT OR | | xnor | Exclusive NOR |

Bitwise

| Op | Meaning | |----|---------| | & \| ^ ~ | Bitwise AND / OR / XOR / NOT | | << >> >>> | Left / right / unsigned right shift |

Type & Predicate

| Op | Meaning | |----|---------| | typeof | Returns type string (unary) | | instanceof | Instance check | | in | Membership / range containment | | istypeof | typeOf(left) === right | | matches | Regex test of left string against right | | between | left > arr[0] && left < arr[1] |

Special / Flow

| Op | Meaning | |----|---------| | \|> | Pipe: x \|> ff(x) | | => | Arrow function body | | .. | Inclusive range: 1..10NovaRange(1,10) | | ... (unary) | Spread / rest | | ... (binary) | Exclusive range: 1...10 → array [1..9] | | ?. | Optional chain — null if left is null | | ? | Ternary start | | :: | Namespace / property access | | >> | Pipe-right: val >> fnfn(val); also used in compose / engage | | delete | Delete variable, property, or subscript | | void | Evaluate and return undefined | | # | Size of right operand (array/object/string length) |

Array / Collection Infix

| Op | Meaning | |----|---------| | intersect | Elements in both arrays | | union | Deduplicated merge | | diff_arr | Elements in left not in right | | zip | [[a0,b0],[a1,b1],...] | | extend | Merge objects / concatenate arrays | | concat | Concatenate | | index | arr index 2 → element at 2 | | step | 0..10 step 2[0,2,4,6,8,10] |

Numeric Infix

| Op | Meaning | |----|---------| | avg | (a + b) / 2 | | diff | Math.abs(a - b) | | ratio | a / b | | mult_of | a % b === 0 | | gcd | Greatest common divisor | | lcm | Least common multiple | | pow | Alias for ** | | bigger / smaller | a > b / a < b aliases |

String Infix

| Op | Meaning | |----|---------| | pad_start | .padStart(right, ' ') | | pad_end | .padEnd(right, ' ') |

Operator Precedence (highest → lowest)

| Prec | Operators | |------|-----------| | 15 | ++ -- (postfix), ?., ::, #:, $:, index | | 14 | Unary: ! ~ - + * ... not typeof void delete | | 13 | ** pow (right-assoc) | | 12 | * / % ratio mult_of gcd lcm | | 11 | + - avg diff pad_start pad_end concat | | 10 | >> << >>> .. step zip | | 9 | < > <= >= bigger smaller | | 8 | == != === !== is isnt equals equals_ignore cmp istypeof matches instanceof in | | 7 | & intersect diff_arr | | 6 | ^ | | 5 | \| union extend | | 4 | && and nand | | 3 | ?? xor xnor | | 2 | \|\| or nor | | 0 | = += -= *= /= %= **= &&= \|\|= ??= => \|> |


Strings & F-Strings

let a = "hello"
let b = 'world'
// Escape sequences: \n \t \r \\ \' \"...etc

// F-strings: f"..." with {} interpolation — any expression allowed
let msg = f"\color{blue}Hello {name}!\reset"
let math = f"2^10 = {2 ** 10}"
let complex = f"items: {arr.map(x => x * 2).join(', ')}"

Control Flow

If / Else

if (cond) { } else if (other) { } else { }

let x = a > b ? a : b                // ternary
let y = value if condition else alt   // postfix if-expression

Unless / Until / Repeat

unless (cond) { }    // runs when cond is false
until (cond) { }     // runs while cond is false
repeat (n) { }       // runs exactly n times

While / Do-While

while (cond) { }
do { } while (cond)

For

for (let i = 0; i < 10; i++) { }
for (let item of array) { }
for (let key in object) { }

Each

each item of array { }
each item, index of array { }

Switch / Case

switch (value) {
  case 1: core.print("one"); break;
  default: core.print("other"); break;
}

Match

match (value) {
  when 1            { }
  when 2, 3         { }              // comma = multi-pattern
  when 1..10        { }              // range
  when "ok" where cond { }          // with guard
  default           { }
}

Loop Control

| Keyword | Effect | |---------|--------| | break | Exit loop | | continue | Next iteration | | skip | Alias for continue (parsed as skip, no-op in execute) | | goback | Throw { __return: scope.__return } — return from function | | end | Throw { __return: undefined } — terminate scope |

Guard

guard (cond) {
  // body runs only when cond is TRUE (passes through)
} else {
  // runs when cond is FALSE
}

When

when cond do { }     // runs body once if cond is truthy
when cond then { }   // same

Where (scoped bindings)

where (base = 100, rate = 0.15) {
  let tax = base * rate
}

With

with (object) {
  // object properties brought into scope
}
with option featureFlag {
  // body runs with flag set; removed after block
}

Loop (Classic iteration)

loop item in iterable { }

Foreach (Classic key-value)

foreach(collection)(key, value) { }
foreach(collection)(key, value, length) { }   // third var = total length

Wait (sync sleep)

wait(1000)   // blocks for 1000ms (Atomics.wait, fallback: child process)

Time Block

time {
  // times this block; prints elapsed ms on completion
}

Functions

Named Functions

func add(a, b) => {
  give a + b
}
function greet(name) {
  return f"Hello {name}"
}

Function Modifiers

Modifiers come after func/function and before the function name. Multiple modifiers can be chained in any order.

| Modifier | Effect | |----------|--------| | async | Marks async; await blocks synchronously via SharedArrayBuffer | | Strict | Throws if argument count doesn't match exactly | | once | Body only runs once; later calls return the first result | | memo | Results memoized by JSON.stringify(args) key | | generator | Returns an iterator; yield inside collects values | | timeout <expr> | Throws if function exceeds expr ms | | defer <stmt> | Executes stmt when function returns (in finally) |

func async load(url) => { }
func Strict strict(a, b) => { }
func once init() => { }
func memo fib(n) => { }
func generator counter() => { }
func async memo cachedFetch(url) => { }
func timeout 5000 riskyOp() => { }
func defer cleanup() riskyOp() => { }

// function keyword works identically
function async load(url) { }
function Strict strict(a, b) { }

in objects, you can have a [expr] quick access method that runs on obj.(...args) example:

let obj = { [expr]: (a, b) => return a + b  }
core.print(obj.(1,5)); // 6

Generator Iterator API

| Method | Description | |--------|-------------| | .next() | { value, done } | | .last() | Step back one | | .current() | Peek without advancing | | .seek(n) | Jump to index n | | .setIndex(n) | Set position | | .currentIndex() | Current index | | .length() | Total collected values | | .at(n) | Value at index n | | [Symbol.iterator] | Makes it iterable in for-of |

Return / Give / Goback

return value   // sets __return, does NOT terminate
give value     // alias — same as return, but terminates the function by throwing
goback         // throws { __return: scope.__return }
yield value    // collects into generator's output array

Arrow Functions

let double = x => x * 2
let add = (a, b) => a + b

Rest & Default Parameters

func sum(...nums) => { give nums.reduce((a, b) => a + b, 0) }
func greet(name = "World") => { }

Block functions:

these auto call on reference, and use the returned value

block coolBlock { core.print(8); return 8;  }
core.print(coolBlock); /* result:
8 //from inside coolBlock
8 //from core.print(coolBlock);
*/
// you can also just run the block using nova.runBlock('coolBlock')

Run bounder:

// run CODE
// or
// run { ... }
// example:
core.print(run let x = 10); //10 because run uses last result if no return/give
core.print(x); // 10
core.print(run { let c = 8; return c; }); // 8
core.print(c); // 8

Pipe Operator

let result = 5 |> double |> addOne

Classes

class Animal {
  name: "unknown"
  speak() { core.print(f"{this.name} speaks") }
}
class Dog extends Animal {
  breed: "mixed"
  speak() { core.print(f"{this.name} barks") }
}
class Doc implements Serializable {
  // must satisfy all non-optional interface members
}

Getter / Setter

class Box {
  _val: 0
  get value()  { give this._val; }
  set value(v) { this._val = v; }
}

Decorators

@logged
@deprecated("use newFn")
class OldClass {
  @readonly
  name: "old"
}

Pattern Matching

match (value) {
  when 90..100           { }   // range — uses NovaRange.includes()
  when "ok"              { }   // scalar — uses ==
  when 1, 2, 3           { }   // any of these — comma-separated
  when x where x > 0    { }   // with guard expression
  default                { }
}

Events

emit "eventName"
emit "eventName", payload

on "eventName" (data) {
  core.print(data)
}

// Internal: fires on every statement execution step
on "nv:tick" () { }

Error Handling

try {
  riskyOp()
} catch (err) {
  core.print(f"caught: {err}")
} finally {
  cleanup()
}

try { riskyOp() } catch { }   // no binding — catch without name

throw "plain string"
throw new Error("typed")

assert condition
assert condition, "failure message"

expt "expectedOutput" from {
  core.print("expectedOutput")
}

Modules & Namespaces

Import

import "path/to/module"
import "path/to/module" as alias
import "path/to/module" as name1, name2
from "module" import func1, func2
import_builtin Air, Chalk, Input, Shema

Export

export myValue
export { name = value, other = value2 }
default myMainExport

Namespace

namespace Math {
  func add(a, b) => { give a + b }
}
Math::add(1, 2)
using namespace Math   // bring all members into scope

Using / Unuse

using featureFlag
using namespace MyNS
unuse featureFlag

Environment Variable

env VAR_NAME   // reads process.env.VAR_NAME; throws if undefined

HTTP & Networking

First-Class HTTP Verbs

get https://api.example.com/users
post https://api.example.com/users({ name: "novac" })
put  https://api.example.com/users/1({ name: "Updated" })
delete https://api.example.com/users/1()
patch  https://api.example.com/users/1({ active: false })
head   https://api.example.com/health
options https://api.example.com/
// Returns: { ok: bool, status: int, body: object|string, text: string }

Sync fetch strategies (tried in order): in-process local Nova server dispatch → curl → PowerShell (Windows) → Node child process.

Fetch

fetch(url)
fetch(url, { method: "POST", headers: { Authorization: "Bearer x" }, body: payload })
fetch(url, opts) => resultVar   // statement form — assigns result

Server Declaration

server(3000) {
  get "/api/users" (req, res) {
    res.json([])
  }
  post "/api/users" (req, res) {
    res.json({ created: true, body: req.body })
  }
  delete "/api/users/:id" (req, res) {
    res.json({ deleted: req.params.id })
  }
}
// req: { method, url, path, headers, body, params, query }
// res: .status(code) .header(k,v) .json(data) .send(data)
// :param values also bound directly into route scope
// If res.json/send never called, route handler's return value is auto-sent

@classic — Classic Compatibility Block

Classic novac keywords are only valid inside an @classic { } block. Outside of one they are plain identifiers and cause a parse error if used as statements. This keeps the core language surface area clean while preserving full backward compatibility for legacy code.

@classic {
  // any classic keyword works in here
  echo "hello"
  keep port = 8080
  gear(500) poll {
    let data = fetch(https://api.example.com/data)
    echo data.body
  }
  engage poll
}

If you use a Classic keyword outside @classic, the parser throws:

ClassicError: 'echo' is a Classic novac keyword and must be used inside an @classic { } block.
Wrap your legacy code: @classic { echo ... }

@classic blocks can appear anywhere a statement is valid — top-level, inside functions, inside if bodies, etc. They share the same scope as their surroundings.

func migrate() => {
  @classic {
    backup session = currentSession
    temp session = "migration" => {
      runMigration()
    }
    retrieve session
  }
}

Classic Keywords (only valid inside @classic { })

Output:

| Keyword | Behavior | |---------|----------| | echo value | process.stdout.write(String(value)) — no newline | | print(val, ...) | Space-joined args + newline | | println(value) | process.stdout.write(value) — no newline | | log(val, ...) | console.log(...args) | | logln(value) | process.stdout.write(value) | | warn "msg" | "Warning: msg\n" | | info "msg" | "INFO: msg\n" | | hello | Prints "Hello, !" | | hello "a", "b" | Prints "Hello, a, and b!" | | banner "text" | Box-drawn ASCII banner |

Scoped Binding:

| Keyword | Behavior | |---------|----------| | temp x = val => { } | Override x for the block duration, then restore | | keep x = val | Set x only if currently null/undefined; silent otherwise |

Macros & Named Code:

| Keyword | Behavior | |---------|----------| | macro NAME = val | Store value in nova.macros and scope | | snippet name { } | Register named snippet; run with nova.runSnippet("name") | | defunc name(args) => { } | Declare function (arrow-body style) | | lambda name = x => expr | Declare arrow function | | compose name = f >> g | Compose functions left-to-right: g(f(x)) | | partial name = fn(arg) | Partial application |

Iteration:

| Keyword | Behavior | |---------|----------| | loop item in iterable { } | For-of style | | foreach(col)(key, val) { } | Key-value iteration | | foreach(col)(key, val, len) { } | With length variable |

State:

| Keyword | Behavior | |---------|----------| | backup x = x | Save value to executor backup store | | retrieve x | Restore from backup store | | addto arr val | Push value to array | | addto map val : key | Set map[key] = val |

Async Tasks:

| Keyword | Behavior | |---------|----------| | gear(ms) name { } | Named loop task; waits ms between iterations | | gear name { } | Named loop task; no wait | | engage a >> b | Run gears left-to-right in continuous loop until break |

Isolation:

| Keyword | Behavior | |---------|----------| | sandbox { } | Run in isolated Node.js vm context; scope vars snapshotted in/out | | infer ("model") => var: "prompt" | Run local AI model via ollama |

Data:

| Keyword | Behavior | |---------|----------| | sstream name => { put val } | Named sync stream with put accumulator | | lend fn src to dst | Merge src function body into dst | | lend m map with fn | Attach fn as method on map object |

Session / Resumable:

| Keyword | Behavior | |---------|----------| | session("name") { } | Store named session body | | enter key type | Log [enter] key type — interactive entry point marker | | resu name(args) => { }, | Resumable: re-parsed and run fresh on each call | | keyfunc name(x) { } { } | Pattern-matched function dispatch |

Annotation / Registry:

| Keyword | Behavior | |---------|----------| | describe "text" | Append to nova.descriptions | | using flagName | Set this.options[flag] = true | | unuse flagName | Delete this.options[flag] | | using namespace MyNS | Bring all namespace members into scope |

Type Ops:

| Keyword | Behavior | |---------|----------| | classify value | Print/return type string | | classify value as Type | Return bool | | rate(v) type | Type-cast (see full table in Type System section) |

Control:

| Keyword | Behavior | |---------|----------| | skip | No-op (parsed, returns undefined) | | end | Terminate current scope: throws { __return: undefined } | | clear | Reset nova.descriptions to [] | | time { } | Time a block; print elapsed ms | | wait(ms) | Sync sleep via Atomics.wait |

Misc:

| Keyword | Behavior | |---------|----------| | expt "val" from { } | Capture stdout; error if actual.trim() !== "val" | | option name = fn | Set runtime option/hook | | env VAR | Read process.env.VAR into scope; throws if undefined | | eval expr | Re-evaluate an already-parsed AST node | | declare (expr) as x | Assign to x | | declare (expr) into x | Append string to x | | declare (expr) as x log | Assign and print | | declare (expr) as x throw | Assign and throw | | declare (expr) as x finalize | Assign and return |

Built-in Global Objects

Always in scope — no import needed.

core

| Member | Description | |--------|-------------| | core.print(v) | process.stdout.write(stringify(v) + '\n') | | core.json | Reference to JSON | | core.getAst() | Returns current program AST | | core.vars | Reference to global scope variables | | core.types | Reference to the TypeRegistry | | core.register(fn) | Wraps a novac function as a native callable | | core.import | Module import function | | core.ndb() | Dev bridge: { exe, scopes, types, eventBus, gears, backups, macros, blocks, snippets, keyfuncs, sessions, resus, namespaces } |

nova (Unified Registry)

Live proxy over all internal registries:

| Member | Description | |--------|-------------| | nova.version | "2.0" | | nova.macros | All defined macros | | nova.blocks | All defined blocks | | nova.snippets | All defined snippets | | nova.gears | All defined gears | | nova.sessions | All defined sessions | | nova.resus | All defined resus | | nova.keyfuncs | All defined keyfuncs | | nova.backups | All backed-up values | | nova.options | Current option flags | | nova.descriptions | Array of all describe annotations | | nova.scope | Reference to global scope | | nova.types.structs | Registered struct names | | nova.types.interfaces | Registered interface names | | nova.types.enums | Registered enum names | | nova.types.traits | Registered trait names | | nova.types.check(val, typeName) | Runtime type check → bool | | nova.types.list() | All registered type names | | nova.events.events() | Active event names | | nova.events.count(ev) | Number of listeners | | nova.events.has(ev) | Has listeners? | | nova.events.clear(ev) | Remove all listeners for event | | nova.events.clearAll() | Remove all events | | nova.setMacro(name, val) | Define/update a macro | | nova.getMacro(name) | Get macro value | | nova.hasMacro(name) | Check if macro exists | | nova.setBlock(name, fn) | Register a native block | | nova.runBlock(name) | Execute a named block | | nova.runSnippet(name, ...args) | Execute a named snippet | | nova.emit(event, val) | Emit event | | nova.on(event, fn) | Listen for event | | nova.eval(code) | Parse and run a novac code string | | nova.inspect(name) | { inScope, isMacro, isBlock, isSnippet, isGear, isSession, isResu, value } | | nova.clearAll() | Clear macros, blocks, snippets, gears, sessions, descriptions |

nvk (Platform / System Namespace)

nvk exposes all system and I/O operations. These replaced the previous statement-based system keywords.

| Member | Description | |--------|-------------| | nvk.notify(title, content) | Termux notification (fallback: stdout) | | nvk.toast(msg) | Termux toast | | nvk.vibrate(ms) | Termux vibrate | | nvk.clipboard(text) | Set clipboard via Termux | | nvk.camera(path) | Take photo via Termux | | nvk.share(path) | Share file via Termux | | nvk.open(path) | Open file via Termux | | nvk.cpu | Array of CPU model strings | | nvk.mem | Total memory e.g. "8.00GB" | | nvk.hostname | os.hostname() | | nvk.uptime | os.uptime() in seconds | | nvk.pid | process.pid | | nvk.arch | os.arch() | | nvk.platform | process.platform | | nvk.osPlatform | os.platform() | | nvk.userInfo | os.userInfo() as NovaObject | | nvk.network | os.networkInterfaces() as NovaObject | | nvk.load | os.loadavg() as NovaArray | | nvk.tmpDir | os.tmpdir() | | nvk.cwd | process.cwd() | | nvk.pathDir(p) | path.dirname(p) | | nvk.pathBase(p) | path.basename(p) | | nvk.pathExt(p) | path.extname(p) | | nvk.pathJoin(...parts) | path.join(...parts) | | nvk.sha256(s) | SHA-256 hex digest | | nvk.randomBytes(n) | N random bytes as hex string | | nvk.uuid() | Random UUID v4 | | nvk.parseURL(raw) | { hostname, pathname, search, protocol, port } | | nvk.readFile(path) | fs.readFileSync(path, 'utf8') | | nvk.writeFile(path, content) | fs.writeFileSync | | nvk.createFile(path, content) | Create/overwrite file | | nvk.deleteFile(path) | fs.unlinkSync | | nvk.listFiles(dir) | fs.readdirSync(dir) as NovaArray | | nvk.exists(path) | fs.existsSync(path) | | nvk.execFile(path) | Parse and run a .nova file | | nvk.sh(cmd) | execSync(cmd) — prints and returns stdout | | nvk.exec(code) | Parse and run a novac code string | | nvk.term(cmd, shell) | Run cmd in specified shell (bash default) | | nvk.banner(text) | Print box-drawn banner | | nvk.warn(msg) | "Warning: msg" | | nvk.info(msg) | "INFO: msg" |

qae (Query Assertion Engine)

All members are functions (a) => bool or (a) => value:

| Member | Description | |--------|-------------| | qae.even(a) / qae.odd(a) | Parity check | | qae.integer(a) | Number.isInteger(a) | | qae.positive(a) / qae.negative(a) / qae.zero(a) | Sign check | | qae.finite_(a) / qae.infinite(a) / qae.nan_(a) | Numeric state | | qae.isnull(a) / qae.defined(a) | Nullability | | qae.trimable(a) / qae.trimmed(a) | Whitespace | | qae.uppercase(a) / qae.lowercase(a) | Case transform | | qae.numeric(a) / qae.alpha(a) / qae.alnum(a) / qae.blank(a) | String class | | qae.palindrome(a) | Is palindrome (case-insensitive, strips spaces) | | qae.empty(a) / qae.nonempty(a) | Emptiness | | qae.unique(a) | Array has no duplicates | | qae.first(a) / qae.last(a) / qae.length(a) | Array/string access | | qae.truthy(a) / qae.falsy(a) | Boolean coercion | | qae.type_(a) | typeof a | | qae.string_(a) / qae.number_(a) / qae.boolean_(a) / qae.object_(a) / qae.array_(a) / qae.function_(a) | Type checks | | qae.prime(a) / qae.composite(a) / qae.oddprime(a) | Number theory | | qae.vowel(a) / qae.consonant(a) | Character class | | qae.sorted(a) / qae.ascending(a) / qae.descending(a) | Order checks | | qae.contains(a, b) / qae.startsWith_(a, b) / qae.endsWith_(a, b) / qae.matches_(a, b) | Containment | | qae.divisible(a, b) / qae.between_(a, lo, hi) | Numeric predicates | | qae.check(a, pred) | Apply function or named qae predicate to a |

novaRegex(pattern, flags)

Semantic regex with <name> placeholders. Returns a JS RegExp.

let emailRe = novaRegex("<email>")
emailRe.test("[email protected]")   // true
novaRegex("<email|url>")  // combined with |

Semantic names: keyword symbol digit nondigit whitespace tab newline start end any wordboundary word variable integer float hex binary space title email url uuid date time color

Global Functions

| Function | Description | |----------|-------------| | typeOf(v) | "number" "string" "bool" "null" "array" "object" "range" "pointer" "function" "struct:Name" "enum:Name" | | typecheck(v, typeName) | Runtime type check → bool | | satisfies(v, ifaceName) | Does value satisfy all required interface members? | | fetch(url, options?) | Sync HTTP → { ok, status, body, text } | | setTimeout(fn, delay) | Schedule function after delay |


Built-in Class Objects

All available globally:

ForLoop(opts)

ForLoop({ from: 1, to: 10 }).map(i => i * i).collect()
ForLoop({ from: 0, to: 20, step: 2 }).toArray()
ForLoop({ from: 1, to: 100 }).do(i => { sum += i }).run()

| Method | Description | |--------|-------------| | .from(n) .to(n) .step(n) | Configure range | | .do(fn) / .each(fn) / .map(fn) | Set body | | .run() | Execute without collecting | | .collect() | Execute and collect results | | .toArray() | Collect index values | | .filter(fn) | Convert to Pipeline then filter | | .pipe() | Convert to Pipeline | | .toStream() | Convert to DataStream |

WhileLoop(opts)

WhileLoop({}).cond(() => n < 32).do(() => { n *= 2 }).maxIter(100).run()

IfBlock(opts)

IfBlock({ cond: score >= 90, then: () => "A" })
  .elseIf(score >= 80, () => "B").else(() => "F").run()

MatchBlock(subject)

MatchBlock(404)
  .when(200, () => "OK")
  .when(404, () => "Not Found")
  .when(90..100, x => "A")
  .when(x => x > 500, x => "server err")
  .default(() => "Unknown")
  .run()

TryCatch()

TryCatch().try(() => { throw "oops" }).catch(e => core.print(e)).finally(() => {}).run()

Pipeline(initial)

Pipeline([1,2,3,4,5]).filter(x => x % 2 == 0).map(x => x*x).take(2).collect()

| Method | Description | |--------|-------------| | .pipe(fn) / .map(fn) | Transform | | .filter(fn) | Filter | | .tap(fn) | Side-effect passthrough | | .flatMap(fn) | Map + flatten | | .reduce(fn, init) | Immediate reduce | | .take(n) / .skip(n) | Slice | | .run() / .collect() | Execute |

FuncDef(opts)

FuncDef({ args: ["a", "b"], body: (a, b) => a + b }).named("add").call(10, 32)

Timer()

let t = Timer(); t.start(); t.lap(); let ms = t.stop()
t.elapsed   // ms since start
t.laps      // NovaArray of lap times

Counter(initial, step)

let c = Counter(0); c.increment(5); c.decrement(2); c.clamp(0, 100); c.value

Stack() / Queue() / LinkedList()

let s = Stack(); s.push(10); s.pop(); s.peek(); s.size; s.empty
let q = Queue(); q.enqueue("a"); q.dequeue(); q.peek(); q.size
let ll = LinkedList(); ll.push("a"); ll.shift(); ll.pop(); ll.unshift("z"); ll.size

State(initial)

let sm = State("idle")
sm.add("idle", "start", "running").add("running", "done", "idle")
sm.onEnter("running", s => {}).onExit("idle", s => {})
sm.dispatch("start")   // true = success
sm.current; sm.history; sm.is("running")

Observable(initial) / Signal(initial)

let obs = Observable(0)
obs.subscribe((newVal, oldVal) => {})
obs.value = 42
let derived = obs.pipe(x => x * 2)

let sig = Signal(1)
let doubled = sig.derive(x => x * 2)
sig.value = 5   // doubled.value = 10 (automatic propagation)
sig.effect(v => {})  // runs immediately and on every change

Validator()

Validator().required().type("number").min(0).max(100).validate(50)
// { valid: true, errors: [], value: 50 }

| Method | Description | |--------|-------------| | .required() .type(t) .min(n) .max(n) | Core validators | | .minLen(n) .maxLen(n) .pattern(re) .email() | String validators | | .custom(fn, msg) | Custom validator | | .validate(v) | Returns { valid, errors: NovaArray, value } |

DataStream(source)

DataStream([5,3,8,1,9]).filter(x => x > 3).sort().reverse().take(3).collect()

| Method | Description | |--------|-------------| | .map .filter .take .skip .flatMap | Transform | | .distinct() | Deduplicate via Set | | .sort(fn?) .reverse() .zip(other) | Reorder | | .collect() .reduce(fn, init) .forEach(fn) | Materialize | | .first() .last() .count() | Inspect |

Transformer() / TransformerJSON() / TransformerBase64()

let jt = TransformerJSON(); jt.to({ x: 1 }); jt.from('{"x":1}')
let b64 = TransformerBase64(); b64.to("Hello!"); b64.from(encoded)
// Static presets: Transformer.upper(), Transformer.trim_(), Transformer.number_()

Router()

let router = Router()
router.on("/api/users/(.*)", id => f"user {id}").default(p => f"404: {p}")
router.dispatch("/api/users/42")   // "user 42"

EventBus()

let bus = EventBus()
bus.on("data", v => {}).once("ready", () => {}).off("data", handler)
bus.emit("data", 42); bus.events()

Memo(fn, keyFn?) / Lazy(fn)

let m = Memo(x => x * x * x)
m.call(5); m.stats; m.clear(); m.invalidate(5)

let lazy = Lazy(() => expensiveOp())
lazy.value; lazy.reset(); lazy.map(fn)

Standard Library (std)

Always available as std.X.

std.Math

abs ceil floor round sqrt pow max min log log2 log10 sin cos tan PI E trunc sign random plus:

| Member | Description | |--------|-------------| | clamp(v, lo, hi) | Math.min(Math.max(v,lo),hi) | | floorTo(a, b) | Floor to nearest multiple of b | | ceilTo(a, b) | Ceil to nearest multiple of b | | factorial(n) | Memoized factorial | | fibonacci(n) | Memoized fibonacci | | divmod(a, b) | [quotient, remainder] |

std.Array

from(it) of(...items) range(start, end, step) isArray(v) fill(n, val) zip(...arrs)

std.Object

keys(o) values(o) entries(o) assign(target, ...srcs) freeze(o) has(o, k) create(proto)

std.String

from(v) padStart padEnd repeat includes startsWith endsWith trim split replace replaceAll toUpper toLower charAt charCodeAt fromCharCode slice indexOf

std.is

std.is.number(v)  std.is.string(v)  std.is.bool(v)  std.is.array(v)
std.is.object(v)  std.is.null(v)    std.is.func(v)   std.is.range(v)
std.is.integer(v) std.is.finite(v)  std.is.NaN(v)

std.fn

identity compose pipe memoize once partial curry noop always flip not

Other std Members

| Member | Description | |--------|-------------| | std.num(v) / std.str(v) / std.bool(v) / std.int(v) | Coercions | | std.print(...) | console.log(...) | | std.error(msg) | Throw an Error | | std.range(start, end, step) | Create NovaRange | | std.fnum(min, max, initial) | Clamped float with .value get/set | | std.fint(min, max, initial) | Clamped integer with .value get/set | | std.convertCase(str, target) | "snake" "camel" "pascal" "kebab" "upper" "lower" | | std.randomWord() | Random pronounceable word | | std.randomName(format?, minLen?, maxLen?) | Capitalized random name | | std.console.* | log warn error info time timeEnd | | std.Date.now() / std.Date.create(...) | Date utilities | | std.json.parse(s) / std.json.stringify(v, sp?) | JSON with NovaObject unwrapping | | std.Promise.* | resolve reject all race allSettled any |


Standard Built-ins (bstd / import_builtin)

Air

Object of references to all novac core modules keyed by filename.

Chalk

ANSI terminal colors (same API as chalk npm package):

import_builtin Chalk
core.print(Chalk.red("error"))
core.print(Chalk.bold.green("success"))

History

File-backed input history stored as .184.<name>:

getKey(name) exists(key) createHistory(key) appendHistory(key, item, mode?, index?) readHistory(key) rawSetHistory(key, array) rawGetHistory(key) clearHistory(key)

Input

Synchronous terminal input with full line-editing:

import_builtin Input
let r = Input.prompt("Name: ")
// r: { result: "typed text", history: [] }
Input.prompt("Pwd: ", { password: true, passwordHash: "*" })
Input.prompt("Cmd: ", true)   // enable history
Input.prompt("Search: ", { completer: buf => "suggestion" })

Features: cursor, history ↑↓, backspace, delete, tab completion, Ctrl+C, password masking.

Shema / ShemaType

import_builtin Shema, ShemaType
let schema = new Shema({ name: ShemaTypes.String, age: ShemaTypes.Int })
schema.validate({ name: "novac", age: 2 })
schema.defaultObj()
new ShemaType("PositiveInt", val => Number.isInteger(val) && val > 0)

Built-in ShemaTypes: Int Float String Boolean Array Object Function Null Undefined Symbol BigInt Date RegExp Shema Any

ConfigFile

import_builtin ConfigFile
let cfg = new ConfigFile("app", "json", false, mySchema)
cfg.writeFull({ host: "localhost", port: 8080 })
cfg.write(["database", "host"], "db.example.com")
cfg.readFull(); cfg.read(["database", "host"])
cfg.interactiveSetup(defaultConfig)

JsDB

JsDB.class(JsClass) JsDB.run(code) JsDB.require JsDB.gt JsDB.fetch(url, opts) JsDB.process JsDB.version JsDB.platform JsDB.env

Storage

import_builtin Storage
Storage.setItem("key", { data: 42 })
Storage.getItem("key")
Storage.removeItem("key")
Storage.clear()

Crypto

import_builtin Crypto
Crypto.hash("data")             // SHA-256 hex
Crypto.hash("data", "sha512")   // any Node.js crypto algo

PathUtils

join resolve basename dirname extname

Base64

Base64.encode(s) Base64.decode(s)

OsUtils

platform homedir() tmpdir() cpus() totalmem() freemem() uptime() openFile(path) (Windows: requestAdminPrivileges())

Zip

import_builtin Zip
await Zip.zip(["a.txt"], "out.zip")
await Zip.unzip("out.zip", "./output/")

Array Methods

Auto-dispatched on any NovaArray:

map filter reduce find findIndex some every flat flatMap sort reverse slice splice indexOf includes join push pop shift unshift concat fill forEach keys values entries at first last random unique count groupBy shuffle min max sum avg product toArray toObject toString | .length (property)


Object Methods

Auto-dispatched on any NovaObject:

keys() values() entries() has(k) assign(...srcs) | .length (property)


String Methods

Auto-dispatched on any string:

toUpperCase toLowerCase trim split includes startsWith endsWith slice indexOf lastIndexOf repeat replace match | .length (property)


Type System & Casting

as Cast

let n = value as int      // Math.trunc(Number(v))
let f = value as float    // Number(v)
let s = value as string   // String(v)
let b = value as bool     // Boolean(v)
let a = value as array    // _toIterable(v) → NovaArray
let st = value as MyStruct

rate() Cast — Full Type Table

| Type | Operation | |------|-----------| | int | Math.trunc(Number(v)) | | float / f64 | Number(v) | | f32 | Math.fround(Number(v)) | | string | String(v) | | bool | Boolean(v) | | bigint | BigInt(Math.trunc(Number(v))) | | u8 | parseInt(v) & 0xFF | | u16 | parseInt(v) & 0xFFFF | | u32 | parseInt(v) >>> 0 | | i8 | (parseInt(v) << 24) >> 24 | | i16 | (parseInt(v) << 16) >> 16 | | i32 | parseInt(v) \| 0 | | char | String(v)[0] | | array | _toIterable(v) → NovaArray | | StructName | createStruct("StructName", v) |

Type Check

typecheck(42, "number")         // true
satisfies(doc, "Serializable")  // true if all required interface members present
classify value                  // returns type string
classify value as TypeName      // returns bool

CLI — novac

novac <file> [args...]          Run a .nova / .nv file
novac test <file>               Check syntax (exit 0 = OK, prints "Syntax OK")
novac format [file]             Format a file (or stdin if no file given)
novac eval <code>               Evaluate a novac code string inline
novac tokens <file>             Print token stream as JSON
novac ast <file>                Print AST as JSON
novac repl                      Start interactive REPL
novac new <project>             Scaffold a new project directory
novac new-kit <dirname>         Scaffold a new kit in current directory
novac init PATH                 Add npm bin to system PATH (writes to shell rc)
novac init build                Bundle project into a .novamod file
novac config get [key]          Read global/project config (all keys if no key given)
novac config set <key> <value>  Write a config key
novac config init               Interactive config setup wizard
novac etc notices               Show runtime notices from stdlib
novac etc describe <file>       Print a human-readable description of a .nova file's AST
novac etc kit <name>            Install a built-in kit into ./nova_modules/<name>
novac etc kit <name> --global   Install a built-in kit into ~/.novac/nova_modules/<name>
novac module install [path]     Install a .novamod bundle (or all deps from nova.config.json)
novac module install [path] -g  Install globally into ~/.novac/nova_modules
novac module list               List all installed modules in nova_modules/
novac module remove <name>      Remove an installed module

Running a File

novac src/main.nova
novac src/main.nova arg1 arg2
novac src/main.nova --port 8080 --debug
novac src/main.nova @addr1 +feature1

All arguments after the file are parsed and exposed as the cli object inside the script (see below).

Argument Parsing Rules

| Argument form | Goes into | |---------------|-----------| | positional (no prefix) | cli.args array | | -flag | cli.options.flag = true | | --flag | cli.options.flag = true | | --flag value | cli.options.flag = "value" | | --flag=value | cli.options.flag = "value" | | @name | cli.addrs.name = true | | +feature | cli.additions.feature = true |

Arguments that start with -, @, or + are excluded from cli.args.

REPL Commands

Inside novac repl:

| Command | Description | |---------|-------------| | .exit | Exit the REPL | | .clear | Clear the screen | | .ast <code> | Print AST of inline code | | .tokens <code> | Print token stream of inline code | | .help | Show REPL help |

History is persisted to ~/.nova_repl_history.

novac new <project>

Scaffolds a project directory with:

<project>/
  src/main.nova          // starter file with print("Hello, Nova!")
  bin/<project>.nv       // entry script (empty, chmod 755)
  nova_modules/          // local module directory
  nova.config.json       // project manifest
  README.md
  .gitignore

nova.config.json fields: name, version, description, author, license, main, srcDir, scripts.run, dependencies, devDependencies.

novac init build

Reads nova.config.json from cwd, collects all .nova/.nv files from srcDir, and bundles them into <name>.novamod — a JSON file with:

{
  "manifest": { "name", "version", "main", ...config },
  "sources": { "relative/path.nova": "source code", ... },
  "buildTime": "ISO timestamp"
}

Install in another project: novac module install ./<name>.novamod

novac etc kit <name>

Installs a built-in kit from novac/kits/<name>/ into nova_modules/. Available built-in kits: kitnovacweb, kitlibfs, kitlibproc.


Built-in cli Object

When a file is run via novac <file> [args...], a cli object is automatically injected into the global scope:

// novac myapp.nova hello world --port 8080 --debug @prod +cache

cli.args          // ["hello", "world"]  — positional args (no flag prefix)
cli.options       // { port: "8080", debug: true }
cli.addrs         // { prod: true }
cli.additions     // { cache: true }
cli.raw           // process.argv — full raw argument array

Full cli Object Shape

| Property | Type | Description | |----------|------|-------------| | cli.args | Array | Positional arguments (no -, @, + prefix) | | cli.options | Object | All -flag, --flag, --flag=val, --flag val arguments | | cli.addrs | Object | All @name arguments, values are true | | cli.additions | Object | All +name arguments, values are true | | cli.raw | Array | process.argv — complete unprocessed argument list |

Usage Examples

// novac server.nova --port 3000 --host localhost
let port = cli.options.port or "8080"
let host = cli.options.host or "0.0.0.0"
server(port) {
  get "/" (req, res) { res.json({ host, port }) }
}
// novac build.nova src/main.nova --minify --output dist/app.js
let inputFile = cli.args[0]
let minify = cli.options.minify or false
let output = cli.options.output or "out.js"
// novac deploy.nova @production +cache
if (cli.addrs.production) {
  core.print("Deploying to production")
}
if (cli.additions.cache) {
  core.print("Cache warming enabled")
}

Threads

func worker() => {
  give heavyComputation()
}

let t = Thread(worker)
t.start()
t.send("message")
t.on_message(msg => { core.print(msg) })
let result = t.join()

| Method / Property | Description | |-------------------|-------------| | .start() | Spawn the worker thread | | .join() | Block until done; applies scope mutations; returns result | | .send(value) | Post message to worker | | .on_message(fn) | Handler for messages from worker | | .result | Final return value | | .error | Error string if worker threw | | .done | Boolean: completed |

Scope snapshot: user-defined variables serialized and sent. Functions serialized via toString(). Worker can write back via thread.set(name, val) — applied atomically on .join().

Read also: NVML readme (./kits/kitnovacweb/README.md)