Documentation

# Using Vazen TOML

Use the @vazen/toml package to read and write Vazen TOML, JSON and PSA files. Any TOML parser can also read a Vazen TOML file.

On this page

- [Example shelf](#example-shelf)
- [Installation](#installation)
- [Read a file](#read)
- [Write a file](#write)
- [JSON files](#json)
- [PSA files](#psa)
- [Effect schemas](#effect)
- [API overview](#api)
- [Validation](#validation)
- [Versions and compatibility](#compatibility)

## Example shelf

This [schema 0.3.0](/spec/0.3.0) spec describes two cereal products on one shelf. Each has three facings across and four packs deep, so the shelf holds 24 units.

[Download example-shelf.vazen.toml](/examples/example-shelf.vazen.toml)

View the complete TOML file

```
# Wholly fictional example.
# All names, identifiers and measurements are invented.
schema = "vazen/spec"
schema_version = "0.3.0"
name = "Example shelf"

[[products]]
name = "Oat cereal"

[[products]]
name = "Cocoa cereal"

[[fixtures]]
[[fixtures.equipment]]
type = "shelf"

[[fixtures.equipment.sites]]
product = { name = "Oat cereal" }
facings = { wide = 3, deep = 4 }

[[fixtures.equipment.sites]]
product = { name = "Cocoa cereal" }
facings = { wide = 3, deep = 4 }
```

The `products` list defines the products. Each site selects one product by name and gives its facing counts. An omitted facing count is one, so `facings = { wide = 3, deep = 4 }` means three across, one high and four deep.

A `vazen/spec` file can omit dimensions and positions. A layout engine adds them to make a `vazen/layout` file, which records the resolved geometry.

## Installation

| Package        | [@vazen/toml 0.4.0 (opens in a new tab)](https://www.npmjs.com/package/@vazen/toml) |
| -------------- | ----------------------------------------------------------------------------------- |
| Reads          | Vazen TOML 0.3.0, spec and layout, as TOML or JSON. PSA files of any version.       |
| Writes         | Vazen TOML 0.3.0, as TOML or JSON. PSA 2017.1.0 to 2024.4.0.                        |
| Default output | Vazen TOML 0.3.0. PSA 2024.4.0.                                                     |
| Details        | [Versions and compatibility](#compatibility)                                        |

Requirements:

- Node.js `>=24`.
- ES modules. The package has no CommonJS build.
- Effect `^4.0.0`, as a peer dependency.

Install the package and Effect:

```
npm install @vazen/toml effect
```

The source is on [GitHub (opens in a new tab)](https://github.com/vazen/toml). The examples on this page use @vazen/toml 0.4.0, Node.js 24 and schema 0.3.0 files.

## Read a file

`decodeVazenProjectFromTomlFile` reads the text of a `.vazen.toml` file. It returns the project and its source.

**Example** (Count the units on the example shelf)

Save this as `count-units.mjs` beside the [example shelf](#example-shelf), then run `node count-units.mjs`.

```
import { readFileSync } from 'node:fs';
import {
  decodeVazenProjectFromTomlFile,
  VAZEN_SITE_DEFAULTS,
} from '@vazen/toml';

const { project } = decodeVazenProjectFromTomlFile(
  readFileSync('example-shelf.vazen.toml', 'utf8'),
);

let units = 0;
for (const fixture of project.fixtures ?? []) {
  for (const equipment of fixture.equipment ?? []) {
    for (const site of equipment.sites ?? []) {
      const { wide, high, deep } = {
        ...VAZEN_SITE_DEFAULTS.facings,
        ...site.facings,
      };
      units += wide * high * deep;
    }
  }
}
console.log(`${project.name}: ${units} units`);
// Output: Example shelf: 24 units
```

The project holds the tables of the file. A key that the file omits is absent, so `site.facings` can be `undefined`. `VAZEN_SITE_DEFAULTS` gives the value of an omitted facing count or orientation.

The source gives the file’s format and declared version, such as `{ declaredVersion: '0.3.0', format: 'vazen-toml' }`. A reader also returns `messages`, a list of structured findings, beside `project` and `source`. A writer writes only the project.

### Read a file with any TOML parser

Any TOML 1.0 parser reads the syntax of a Vazen TOML file. This example uses the parser in Python 3.11 or later. Save it as `read_plan.py` beside the file, then run `python3 read_plan.py`.

```
import tomllib

with open("example-shelf.vazen.toml", "rb") as source:
    plan = tomllib.load(source)

units = 0
for shelf in plan["fixtures"][0]["equipment"]:
    for site in shelf["sites"]:
        facings = site["facings"]
        units += (
            facings.get("wide", 1)
            * facings.get("high", 1)
            * facings.get("deep", 1)
        )
print(plan["name"])
print(f"{units} units")
```

The output is:

```
Example shelf
24 units
```

A general parser checks only the syntax. The package also checks the shape of each table. [Validation](#validation) says what it checks.

## Write a file

`encodeTomlFileFromVazenProject` writes a project as the text of a `.vazen.toml` file.

**Example** (Write a spec with one site)

```
import { encodeTomlFileFromVazenProject } from '@vazen/toml';

const text = encodeTomlFileFromVazenProject({
  project: {
    schema: 'vazen/spec',
    schema_version: '0.3.0',
    name: 'Example shelf',
    products: [{ name: 'Oat cereal' }],
    fixtures: [
      {
        equipment: [
          {
            type: 'shelf',
            sites: [
              { product: { name: 'Oat cereal' }, facings: { wide: 3, deep: 4 } },
            ],
          },
        ],
      },
    ],
  },
});
```

The text is:

```
schema = "vazen/spec"
schema_version = "0.3.0"
name = "Example shelf"

[[products]]
name = "Oat cereal"

[[fixtures]]
[[fixtures.equipment]]
type = "shelf"

[[fixtures.equipment.sites]]
[fixtures.equipment.sites.product]
name = "Oat cereal"

[fixtures.equipment.sites.facings]
wide = 3
deep = 4
```

A project must have `schema` and `schema_version`. The writer writes schema 0.3.0, unless the `version` option names another version that `VazenVersion` lists. It refuses a project that a reader would refuse as a file.

## JSON files

`decodeVazenProjectFromJsonFile` and `encodeJsonFileFromVazenProject` read and write the same tables as JSON, in a `.vazen.json` file. JSON suits a program or an API. TOML suits a file that people keep and edit. The [specification](/spec/0.3.0#json) gives the rules for dates, `null`, `nan` and `inf`.

**Example** (Write a date as JSON and read it back)

```
import {
  decodeVazenProjectFromJsonFile,
  decodeVazenProjectFromTomlFile,
  encodeJsonFileFromVazenProject,
} from '@vazen/toml';

const { project } = decodeVazenProjectFromTomlFile(`schema = "vazen/spec"
schema_version = "0.3.0"
reviewed = 2026-09-27
`);
const json = encodeJsonFileFromVazenProject({ project });
console.log(json);
/*
Output:
{
  "schema": "vazen/spec",
  "schema_version": "0.3.0",
  "reviewed": "2026-09-27"
}
*/

const read = decodeVazenProjectFromJsonFile(json);
console.log(read.project.reviewed instanceof Date);
// Output: true
```

The JSON reader also refuses an integer outside JavaScript’s safe range, as the TOML reader does.

## PSA files

[PSA files](/psa) shows how to convert a ProSpace PSA file to JSON or Vazen TOML and write it back. It also says what a conversion keeps.

## Effect schemas

The file functions throw a `SchemaError`. To get a `Result` or an `Effect`, use the Effect schemas behind them.

| Schema                     | Encoded      | Type           |
| -------------------------- | ------------ | -------------- |
| `VazenProjectFromTomlText` | `string`     | `VazenProject` |
| `VazenProjectFromJsonText` | `string`     | `VazenProject` |
| `VazenProjectFromPsaText`  | `string`     | `VazenProject` |
| `VazenProjectFromPsaBytes` | `Uint8Array` | `VazenProject` |
| `VazenProjectFromPsaData`  | `PsaData`    | `VazenProject` |
| `PsaDataFromPsaText`       | `string`     | `PsaData`      |
| `PsaDataFromPsaBytes`      | `Uint8Array` | `PsaData`      |

A `VazenProject` differs from the tables of a file:

- An optional value is an `Option`.
- Keys that the specification does not define are in the `attributes` of each object.
- `stage` is `'spec'` or `'layout'`.
- `source` is part of the project. So a PSA schema writes the version of the PSA file that the project came from, if that version can hold the project.

**Example** (Decode a file to a Result)

```
import { readFileSync } from 'node:fs';
import { Option, Result, Schema } from 'effect';
import { VazenProjectFromTomlText } from '@vazen/toml';

const result = Schema.decodeResult(VazenProjectFromTomlText)(
  readFileSync('example-shelf.vazen.toml', 'utf8'),
);
if (Result.isSuccess(result)) {
  const project = result.success;
  console.log(project.stage, Option.getOrElse(project.name, () => 'Untitled'));
} else {
  console.error(result.failure.message);
}
// Output: spec Example shelf
```

## API overview

No function reads or writes the file system. Your code reads and writes the files.

| Function                         | Input                   | Output                |
| -------------------------------- | ----------------------- | --------------------- |
| `decodeVazenProjectFromTomlFile` | `.vazen.toml` text      | `{ project, source }` |
| `encodeTomlFileFromVazenProject` | `{ project, version? }` | `.vazen.toml` text    |
| `decodeVazenProjectFromJsonFile` | `.vazen.json` text      | `{ project, source }` |
| `encodeJsonFileFromVazenProject` | `{ project, version? }` | `.vazen.json` text    |
| `decodeVazenProjectFromPsaFile`  | PSA bytes               | `{ project, source }` |
| `encodePsaFileFromVazenProject`  | `{ project, version? }` | PSA bytes             |
| `decodePsaDataFromPsaFile`       | PSA bytes               | `PsaData`             |
| `encodePsaFileFromPsaData`       | `{ data, version? }`    | PSA bytes             |

The package also exports:

- `VazenVersion` and `PsaVersion`, the versions each writer accepts.
- `VAZEN_SITE_DEFAULTS` and the `PSA_*_DEFAULTS`, the values of omitted keys and empty cells.
- `VazenTomlProjectV0_3_0`, the tables of a 0.3.0 file, and the types of `VazenProject`, `PsaData` and their parts.

This release does not check whole-file or spatial rules, and does not upgrade a file to another schema version.

### Errors

Each function throws a `SchemaError` when it cannot read or write. The message is a tree that ends at the problem. PSA reading makes a best effort and returns a project satisfying its schema, with recoverable issues in `messages`. Writers enforce the destination format’s requirements.

**Example** (Read a file of an unsupported version)

```
import { decodeVazenProjectFromTomlFile } from '@vazen/toml';

decodeVazenProjectFromTomlFile(`schema = "vazen/spec"
schema_version = "0.9.0"
`);
/*
throws:
SchemaError(Expected VazenVersion, got "0.9.0"
  at ["schema_version"])
*/
```

## Validation

Reading checks the shape of each table: its keys, their types and their allowed values. For example, a product must have a `name`, and a `gtin` must have 8, 12, 13 or 14 digits.

Reading does not check rules that need the whole file, such as each product selector matching exactly one product. It does not check spatial rules, such as equipment containment or overlapping product placements. It does not repair a file.

We are developing optional spatial checks. They will be published with the schema they apply to, and use that schema’s version. Their result will be separate from base conformance.

Validation reports problems. A repair changes the plan, so it must be requested explicitly. The [specification](/spec/0.3.0) is the source for the requirements of each release.

## Versions and compatibility

The **schema version** identifies the data contract. A **package version** identifies one implementation. They change independently, and npm and Python releases can support the same schema.

| Artifact                                                                                  | Status    | Version                             |
| ----------------------------------------------------------------------------------------- | --------- | ----------------------------------- |
| [Specification](/spec/0.3.0)                                                              | Published | 0.3.0                               |
| [JavaScript / TypeScript (opens in a new tab)](https://www.npmjs.com/package/@vazen/toml) | Published | @vazen/toml 0.4.0                   |
| Python                                                                                    | Planned   | Its own releases and schema support |

@vazen/toml 0.4.0 supports:

| Capability                            | Support                                                                                                                            |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Read `vazen/spec` and `vazen/layout`  | 0.3.0, as TOML or JSON                                                                                                             |
| Write `vazen/spec` and `vazen/layout` | 0.3.0, as TOML or JSON. Default 0.3.0.                                                                                             |
| Read PSA                              | Any version. Column counts are known for the versions the writer accepts. A row with another count is reported as `UnlistedWidth`. |
| Write PSA                             | 2017.1.0, 2017.2.0, 2019.1.0, 2019.1.1, 2020.1.0, 2020.1.1, 2021.1.2, 2023.1.0, 2024.1.0, 2024.4.0. Default 2024.4.0.              |
| Check whole-file rules                | Not supported                                                                                                                      |
| Check spatial rules                   | Not supported                                                                                                                      |
| Upgrade to another schema version     | Not supported                                                                                                                      |

Each package release states the schemas it reads and writes, and its default output. A reader for a version does not mean a writer or an upgrade path for it.

The `schema_version` in a file does not identify the npm package, the Python package or the TOML version. Older specifications keep their own versioned URLs.
