zena:path

zena
import {…} from 'zena:path';

File path manipulation and inspection for forward-slash delimited paths.

This module provides two complementary APIs:

  1. Standalone string functions: (join, dirname, basename, extname, normalize, relative, split, stripPrefix) operate directly on standard String values.
  2. Strongly typed path classes: (Path, AbsolutePath, RelativePath, PathSegment, FilePath, DirectoryPath) wrap String as extension classes to enforce path invariants at compile time with zero runtime allocation overhead.

Strongly Typed Paths ​

Using plain strings for paths can lead to subtle bugs, such as passing a relative path where an absolute path is required or joining two absolute paths together. The typed path hierarchy models path distinctions in the type system:

  • Path: Base extension class for any path.
  • AbsolutePath: A path guaranteed to start with /.
  • RelativePath: A path guaranteed not to start with /.
  • PathSegment: A single non-empty relative component containing no /.
  • FilePath: A path representing a file.
  • DirectoryPath: A path representing a directory, with fluent child navigation.

Because these are Zena extension classes on String, they are erased at runtime. A Path or AbsolutePath is represented as an unboxed String in WebAssembly memory with no object wrappers and no virtual method tables.

Constructing and Validating Paths ​

Standard constructors validate format at runtime and throw Error on invalid input:

zena
let abs = new AbsolutePath('/usr/local/bin');
let rel = new RelativePath('src/main.zena');
let seg = new PathSegment('main.zena');

When input is already known to be valid (for example, output from an internal helper), the unchecked constructors bypass validation checks:

zena
let fastAbs = new AbsolutePath.unchecked('/etc/hosts');

Type-Safe Path Operations ​

Typed paths enforce sound combinations. For example, AbsolutePath.join() requires a RelativePath parameter and returns an AbsolutePath:

zena
let root = new AbsolutePath('/home/user');
let sub = new RelativePath('projects/zena');
let projectDir = root.join(sub); // AbsolutePath: '/home/user/projects/zena'

Conversions ​

A general Path can be converted to more specific types:

  • toAbsolutePath() and toRelativePath() validate at runtime and throw Error if the path does not match the target format.
  • toFilePath() and toDirectoryPath() are erased compile-time casts with no runtime checks.

Examples ​

zena
import { join, dirname, basename, extname, AbsolutePath, RelativePath, DirectoryPath } from 'zena:path';

// Standalone functions on strings:
let fullPath = join(['src/lib', 'index.zena']);
let dir = dirname(fullPath);
let file = basename(fullPath);
let ext = extname(fullPath);

// Strongly typed path classes:
let project = new DirectoryPath('/workspace/app');
let src = project.dir('src');
let mainFile = src.file('main.zena');
let extStr = mainFile.extension; // '.zena'

Classes

Path

zena
extension class Path

A strongly-typed representation of a filesystem path.

Path is the base extension class for all path types, extending String. It provides methods for path inspection, normalization, extension handling, and navigation.

Subclasses ​

  • AbsolutePath: An absolute path starting with /.
  • RelativePath: A relative path not starting with /.
  • PathSegment: A single non-empty relative component containing no /. (Subclass of RelativePath.)
  • FilePath: A path representing a file, with file-specific properties like extension.
  • DirectoryPath: A path representing a directory, with fluent methods (file, dir, directory) for building child paths.

Performance Characteristics ​

All path classes are Zena extension classes on String. At runtime, they are erased to plain strings with zero heap allocation overhead, no wrappers, and no virtual dispatch.

Constructors
zena
new(value: String)
#

Constructs a Path from a string.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

Properties
zena
value: String { get; }
#

The underlying string value of this path.

Zero-cost getter returning the underlying String directly.

zena
length: i32 { get; }
#

Length of the path in bytes.

zena
isAbsolute: boolean { get; }
#

True if this path is absolute (starts with '/').

Methods
zena
normalize(): this
#

Returns a normalized copy of this path, preserving its concrete type.

zena
dirname(): Path
#

Returns the directory portion of this path.

zena
parent(): Path
#

Alias for dirname().

zena
basename(suffix: String = ''): String
#

Returns the last component of this path.

zena
extname(): String
#

Returns the file extension, or '' if none.

zena
withExtension(newExt: String): this
#

Returns a copy with the extension replaced or appended, preserving concrete type.

zena
withoutExtension(): this
#

Returns a copy with the extension removed, preserving concrete type.

zena
join(rel: RelativePath): Path
#

Joins a relative path to this path.

zena
split(): FixedArray<PathSegment>
#

Splits this path into non-empty segments.

zena
relative(to: Path): RelativePath
#

Computes the relative path from this path to to.

zena
stripPrefix(prefix: Path): RelativePath | null
#

Strips prefix from this path if it begins with prefix.

zena
toAbsolutePath(): AbsolutePath
#

Converts this path to an AbsolutePath.

Performs a runtime check to verify the path starts with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is not absolute (does not start with '/').

zena
toRelativePath(): RelativePath
#

Converts this path to a RelativePath.

Performs a runtime check to verify the path does not start with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is absolute (starts with '/').

zena
toFilePath(): FilePath
#

Converts this path to a FilePath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toDirectoryPath(): DirectoryPath
#

Converts this path to a DirectoryPath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toString(): String
#

Returns the underlying string.

Zero-cost erased cast returning the underlying String directly with no copy or allocation.

AbsolutePath

zena
extension class AbsolutePath extends Path
Extends Path

An absolute filesystem path (starts with '/').

Subclass of Path. Enforces that the path begins with a leading forward slash.

Constructors
zena
new(value: String)
#

Constructs an AbsolutePath, validating that value starts with '/'.

Performs a runtime check. Zero heap allocation (erased at runtime).

Throws Error

If value does not start with '/'.

zena
new unchecked(value: String)
#

Constructs an AbsolutePath without runtime validation.

Use when value is already known to start with '/'. Zero-cost compile-time cast with no runtime check and no allocation.

Methods
zena
join(rel: RelativePath): AbsolutePath
#

Joins a relative path to this absolute path, producing an AbsolutePath.

zena
dirname(): AbsolutePath
#

Returns the directory portion of this path as an AbsolutePath.

zena
parent(): AbsolutePath
#

Alias for dirname().

zena
toAbsolutePath(): AbsolutePath
#

Returns this path as an AbsolutePath.

Zero-cost identity operation (return this;). Performs no runtime check and no allocation.

15 inherited members
From Path
Properties
zena
value: String { get; }
#

The underlying string value of this path.

Zero-cost getter returning the underlying String directly.

zena
length: i32 { get; }
#

Length of the path in bytes.

zena
isAbsolute: boolean { get; }
#

True if this path is absolute (starts with '/').

Methods
zena
normalize(): this
#

Returns a normalized copy of this path, preserving its concrete type.

zena
basename(suffix: String = ''): String
#

Returns the last component of this path.

zena
extname(): String
#

Returns the file extension, or '' if none.

zena
withExtension(newExt: String): this
#

Returns a copy with the extension replaced or appended, preserving concrete type.

zena
withoutExtension(): this
#

Returns a copy with the extension removed, preserving concrete type.

zena
split(): FixedArray<PathSegment>
#

Splits this path into non-empty segments.

zena
relative(to: Path): RelativePath
#

Computes the relative path from this path to to.

zena
stripPrefix(prefix: Path): RelativePath | null
#

Strips prefix from this path if it begins with prefix.

zena
toRelativePath(): RelativePath
#

Converts this path to a RelativePath.

Performs a runtime check to verify the path does not start with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is absolute (starts with '/').

zena
toFilePath(): FilePath
#

Converts this path to a FilePath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toDirectoryPath(): DirectoryPath
#

Converts this path to a DirectoryPath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toString(): String
#

Returns the underlying string.

Zero-cost erased cast returning the underlying String directly with no copy or allocation.

RelativePath

zena
extension class RelativePath extends Path
Extends Path

A relative filesystem path (does not start with '/').

Subclass of Path. Enforces that the path does not begin with a leading forward slash.

Constructors
zena
new(value: String)
#

Constructs a RelativePath, validating that value does not start with '/'.

Performs a runtime check. Zero heap allocation (erased at runtime).

Throws Error

If value starts with '/'.

zena
new unchecked(value: String)
#

Constructs a RelativePath without runtime validation.

Use when value is already known not to start with '/'. Zero-cost compile-time cast with no runtime check and no allocation.

Methods
zena
join(rel: RelativePath): RelativePath
#

Joins a relative path to this relative path, producing a RelativePath.

zena
dirname(): RelativePath
#

Returns the directory portion of this path as a RelativePath.

zena
parent(): RelativePath
#

Alias for dirname().

zena
toRelativePath(): RelativePath
#

Returns this path as a RelativePath.

Zero-cost identity operation (return this;). Performs no runtime check and no allocation.

15 inherited members
From Path
Properties
zena
value: String { get; }
#

The underlying string value of this path.

Zero-cost getter returning the underlying String directly.

zena
length: i32 { get; }
#

Length of the path in bytes.

zena
isAbsolute: boolean { get; }
#

True if this path is absolute (starts with '/').

Methods
zena
normalize(): this
#

Returns a normalized copy of this path, preserving its concrete type.

zena
basename(suffix: String = ''): String
#

Returns the last component of this path.

zena
extname(): String
#

Returns the file extension, or '' if none.

zena
withExtension(newExt: String): this
#

Returns a copy with the extension replaced or appended, preserving concrete type.

zena
withoutExtension(): this
#

Returns a copy with the extension removed, preserving concrete type.

zena
split(): FixedArray<PathSegment>
#

Splits this path into non-empty segments.

zena
relative(to: Path): RelativePath
#

Computes the relative path from this path to to.

zena
stripPrefix(prefix: Path): RelativePath | null
#

Strips prefix from this path if it begins with prefix.

zena
toAbsolutePath(): AbsolutePath
#

Converts this path to an AbsolutePath.

Performs a runtime check to verify the path starts with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is not absolute (does not start with '/').

zena
toFilePath(): FilePath
#

Converts this path to a FilePath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toDirectoryPath(): DirectoryPath
#

Converts this path to a DirectoryPath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toString(): String
#

Returns the underlying string.

Zero-cost erased cast returning the underlying String directly with no copy or allocation.

PathSegment

zena
extension class PathSegment extends RelativePath
Extends RelativePath

A single path segment (non-empty, contains no '/').

Subclass of RelativePath. Represents an individual component within a path.

Constructors
zena
new(value: String)
#

Constructs a PathSegment, validating that value is non-empty and contains no '/'.

Performs a runtime check. Zero heap allocation (erased at runtime).

Throws Error

If value is empty or contains '/'.

zena
new unchecked(value: String)
#

Constructs a PathSegment without runtime validation.

Use when value is already known to be a non-empty string with no '/'. Zero-cost compile-time cast with no runtime check and no allocation.

Methods
zena
split(): FixedArray<PathSegment>
#

A single segment splits into an array containing only itself.

18 inherited members
From Path
Properties
zena
value: String { get; }
#

The underlying string value of this path.

Zero-cost getter returning the underlying String directly.

zena
length: i32 { get; }
#

Length of the path in bytes.

zena
isAbsolute: boolean { get; }
#

True if this path is absolute (starts with '/').

Methods
zena
normalize(): this
#

Returns a normalized copy of this path, preserving its concrete type.

zena
basename(suffix: String = ''): String
#

Returns the last component of this path.

zena
extname(): String
#

Returns the file extension, or '' if none.

zena
withExtension(newExt: String): this
#

Returns a copy with the extension replaced or appended, preserving concrete type.

zena
withoutExtension(): this
#

Returns a copy with the extension removed, preserving concrete type.

zena
relative(to: Path): RelativePath
#

Computes the relative path from this path to to.

zena
stripPrefix(prefix: Path): RelativePath | null
#

Strips prefix from this path if it begins with prefix.

zena
toAbsolutePath(): AbsolutePath
#

Converts this path to an AbsolutePath.

Performs a runtime check to verify the path starts with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is not absolute (does not start with '/').

zena
toFilePath(): FilePath
#

Converts this path to a FilePath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toDirectoryPath(): DirectoryPath
#

Converts this path to a DirectoryPath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toString(): String
#

Returns the underlying string.

Zero-cost erased cast returning the underlying String directly with no copy or allocation.

zena
join(rel: RelativePath): RelativePath
#

Joins a relative path to this relative path, producing a RelativePath.

zena
dirname(): RelativePath
#

Returns the directory portion of this path as a RelativePath.

zena
parent(): RelativePath
#

Alias for dirname().

zena
toRelativePath(): RelativePath
#

Returns this path as a RelativePath.

Zero-cost identity operation (return this;). Performs no runtime check and no allocation.

FilePath

zena
extension class FilePath extends Path
Extends Path

A strongly-typed filesystem path representing a file.

Subclass of Path. Provides access to file-specific properties like extension.

Constructors
zena
new(value: String)
#

Constructs a FilePath from a string.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

Properties
zena
extension: String { get; }
#

The file extension (including leading dot), or '' if none.

19 inherited members
From Path
Properties
zena
value: String { get; }
#

The underlying string value of this path.

Zero-cost getter returning the underlying String directly.

zena
length: i32 { get; }
#

Length of the path in bytes.

zena
isAbsolute: boolean { get; }
#

True if this path is absolute (starts with '/').

Methods
zena
normalize(): this
#

Returns a normalized copy of this path, preserving its concrete type.

zena
dirname(): Path
#

Returns the directory portion of this path.

zena
parent(): Path
#

Alias for dirname().

zena
basename(suffix: String = ''): String
#

Returns the last component of this path.

zena
extname(): String
#

Returns the file extension, or '' if none.

zena
withExtension(newExt: String): this
#

Returns a copy with the extension replaced or appended, preserving concrete type.

zena
withoutExtension(): this
#

Returns a copy with the extension removed, preserving concrete type.

zena
join(rel: RelativePath): Path
#

Joins a relative path to this path.

zena
split(): FixedArray<PathSegment>
#

Splits this path into non-empty segments.

zena
relative(to: Path): RelativePath
#

Computes the relative path from this path to to.

zena
stripPrefix(prefix: Path): RelativePath | null
#

Strips prefix from this path if it begins with prefix.

zena
toAbsolutePath(): AbsolutePath
#

Converts this path to an AbsolutePath.

Performs a runtime check to verify the path starts with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is not absolute (does not start with '/').

zena
toRelativePath(): RelativePath
#

Converts this path to a RelativePath.

Performs a runtime check to verify the path does not start with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is absolute (starts with '/').

zena
toFilePath(): FilePath
#

Converts this path to a FilePath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toDirectoryPath(): DirectoryPath
#

Converts this path to a DirectoryPath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toString(): String
#

Returns the underlying string.

Zero-cost erased cast returning the underlying String directly with no copy or allocation.

DirectoryPath

zena
extension class DirectoryPath extends Path
Extends Path

A strongly-typed filesystem path representing a directory.

Subclass of Path. Provides convenience methods for navigating and creating child paths.

Constructors
zena
new(value: String)
#

Constructs a DirectoryPath from a string.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

Methods
zena
join(segment: String): Path
#

Joins a child path segment to this directory.

zena
file(name: String): FilePath
#

Joins a file name to this directory, returning a FilePath.

zena
dir(name: String): DirectoryPath
#

Joins a child directory name to this directory, returning a DirectoryPath.

zena
directory(name: String): DirectoryPath
#

Joins a child directory name to this directory, returning a DirectoryPath.

18 inherited members
From Path
Properties
zena
value: String { get; }
#

The underlying string value of this path.

Zero-cost getter returning the underlying String directly.

zena
length: i32 { get; }
#

Length of the path in bytes.

zena
isAbsolute: boolean { get; }
#

True if this path is absolute (starts with '/').

Methods
zena
normalize(): this
#

Returns a normalized copy of this path, preserving its concrete type.

zena
dirname(): Path
#

Returns the directory portion of this path.

zena
parent(): Path
#

Alias for dirname().

zena
basename(suffix: String = ''): String
#

Returns the last component of this path.

zena
extname(): String
#

Returns the file extension, or '' if none.

zena
withExtension(newExt: String): this
#

Returns a copy with the extension replaced or appended, preserving concrete type.

zena
withoutExtension(): this
#

Returns a copy with the extension removed, preserving concrete type.

zena
split(): FixedArray<PathSegment>
#

Splits this path into non-empty segments.

zena
relative(to: Path): RelativePath
#

Computes the relative path from this path to to.

zena
stripPrefix(prefix: Path): RelativePath | null
#

Strips prefix from this path if it begins with prefix.

zena
toAbsolutePath(): AbsolutePath
#

Converts this path to an AbsolutePath.

Performs a runtime check to verify the path starts with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is not absolute (does not start with '/').

zena
toRelativePath(): RelativePath
#

Converts this path to a RelativePath.

Performs a runtime check to verify the path does not start with '/'. Zero heap allocation (extension classes are erased at runtime).

Throws Error

If this path is absolute (starts with '/').

zena
toFilePath(): FilePath
#

Converts this path to a FilePath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toDirectoryPath(): DirectoryPath
#

Converts this path to a DirectoryPath.

Zero-cost compile-time cast. Performs no runtime validation check and no heap allocation.

zena
toString(): String
#

Returns the underlying string.

Zero-cost erased cast returning the underlying String directly with no copy or allocation.

Functions

isAbsolute

zena
function isAbsolute(path: String): boolean

Returns true if path is an absolute path (starts with '/').

split

zena
function split(path: String): FixedArray<PathSegment>

Splits path by the separator into non-empty segments using a two-pass approach that returns a fixed-size array of PathSegments.

zena
split('/foo/bar/baz') // ['foo', 'bar', 'baz']
split('a//b')         // ['a', 'b']

normalize

zena
function normalize(path: String): String

Normalizes path, resolving '.' and '..' segments and collapsing multiple consecutive slashes.

Leading '/' is preserved for absolute paths. Leading '..' segments are preserved for relative paths that navigate above their starting point.

zena
normalize('/a/b/../c/./d') // '/a/c/d'
normalize('a//b/./c')      // 'a/b/c'
normalize('')              // '.'

dirname

zena
function dirname(path: String): String

Returns the directory name of path, similar to POSIX dirname. Trailing slashes are ignored.

zena
dirname('/a/b/c') // '/a/b'
dirname('/a')     // '/'
dirname('a/b')    // 'a'
dirname('a')      // '.'
dirname('/')      // '/'

basename

zena
function basename(path: String, suffix: String = ''): String

Returns the last component of path, similar to POSIX basename. If suffix is provided and the basename ends with suffix (and is longer than suffix), the suffix is removed.

zena
basename('/a/b/c.zena')         // 'c.zena'
basename('/a/b/c.zena', '.zena') // 'c'
basename('/a/b/')                // 'b'
basename('/')                    // '/'

extname

zena
function extname(path: String): String

Returns the extension of path from the last '.' to the end of the filename. Returns an empty string if there is no '.' or if the '.' is the first character of the filename (e.g. '.gitignore').

zena
extname('foo.zena')     // '.zena'
extname('foo.bar.zena') // '.zena'
extname('foo')          // ''
extname('.gitignore')   // ''
extname('foo.')         // '.'

withExtension

zena
function withExtension(path: String, newExt: String): String

Replaces or appends the extension of path. If newExt does not start with '.', one is automatically added (unless newExt is empty, in which case the extension is removed).

zena
withExtension('main.wasm', 'cwasm')  // 'main.cwasm'
withExtension('main.wasm', '.cwasm') // 'main.cwasm'
withExtension('main.wasm', '')       // 'main'
withExtension('main', '.wasm')       // 'main.wasm'

join2

zena
function join2(a: String, b: String): String

Joins two path segments and normalizes the result. If b is an absolute path, it replaces a.

join

zena
function join(parts: Array<String>): String

Joins all given path segments together and normalizes the resulting path. If any segment is absolute, preceding segments are ignored.

stripPrefix

zena
function stripPrefix(path: String, prefix: String): String | null

Strips prefix from path if path begins with prefix at a directory boundary. Returns the remainder (without leading slash), or null if prefix does not match.

zena
stripPrefix('/a/b/c', '/a/b')  // 'c'
stripPrefix('/a/b/c', '/a/b/') // 'c'
stripPrefix('/a/bc', '/a/b')   // null
stripPrefix('/a/b', '/a/b')    // ''

relative

zena
function relative(from: String, to: String): String

Computes the relative path from from to to. If both paths resolve to the same location, returns '.'.

zena
relative('/a/b', '/a/b/c/d') // 'c/d'
relative('/a/b/c', '/a/b')    // '..'
relative('/a/b/c', '/a/d/e')  // '../../d/e'

Variables

separator

zena
let separator: String

The path segment separator ('/').

delimiter

zena
let delimiter: String

The platform path list delimiter (':' in POSIX/WASI).