Modules and Packages

js/modules-and-packages.md

Fino uses ES modules for application code, built-in runtime APIs, and installed packages. The module loader understands local specifiers, public fino:* built-ins, selected web-style globals, and package mappings generated by fino install.

Local Modules

Use relative imports for files in your project:

import { loadConfig } from './config.ts';
import { createServer } from './server.ts';

Prefer explicit extensions in application code. Fino's own runtime modules use .ts files and the docs link directly to those source modules.

Local file:// URLs are accepted for local filesystem paths, including percent-encoded path characters such as spaces. Malformed percent escapes and non-local file://host/... URLs are rejected. Directory imports are not expanded to index files; import the file you want directly.

Built-In Modules

Use fino:* specifiers for runtime APIs:

import { serveHttp } from 'fino:net/http/server';
import { Database } from 'fino:database/sqlite';
import { parse as parseCsv } from 'fino:format/csv';
import { Watcher } from 'fino:file/watch';

The shape of the specifier mirrors the public module location under js/. Examples:

Only import documented public modules from applications. Other runtime specifier forms are not part of the supported application API.

Public API Boundaries

Application code should depend on:

Application code should avoid:

That boundary keeps code portable across runtime changes and lets generated docs represent the supported API surface.

Because capabilities arrive through imports, this boundary is also a security surface: child realms can be restricted to a narrow set of importable modules through import rules. See the runtime model and Import Capabilities.

Installed Packages

Initialize a package file:

fino init --yes

Install dependencies:

fino install yaml semver

Fino installs package contents under .fino/packages and writes .fino/package-map.json. The package map tells the loader which files satisfy bare package imports and exported subpaths.

After installation, import packages by their package names:

import semver from 'semver';

console.log(semver.satisfies('1.4.2', '^1.0.0'));

Run fino install again after changing dependencies in package.json.

The package map is the supported package-resolution surface. Fino does not walk node_modules or evaluate package.json files at import time; the installer expands npm metadata into the map when packages are installed, and the loader resolves bare imports from the map alone. The package map is also the current reproducibility artifact: commit it when your project expects reproducible bare-import resolution.

JSON files are imported as modules with a default export. Fino currently accepts JSON imports with or without Node-style import attributes; strict JSON import-attribute enforcement is not part of this release baseline.

Current Installer Limits

fino install resolves packages from FINO_NPM_REGISTRY when it is set, or from the public npm registry by default. Current limits to be aware of:

Full npm resolver parity is outside this baseline.

Synthetic Modules

SyntheticModule installs a same-realm virtual module under a bare specifier. Installing the same SyntheticModule twice throws, and installing a different synthetic module at an already-installed specifier also throws. After a module is uninstalled, that specifier can be installed again and subsequent imports see the new exports.