Skip to main content
TOML

PSA files

Read and convert ProSpace PSA planogram files

The free @vazen/toml package reads ProSpace .psa planogram files from JDA and Blue Yonder Space Planning. It converts them to JSON or Vazen TOML, and writes them back as PSA. It runs without Space Planning.

Is this your file?

A ProSpace PSA file is text. It starts with these lines:

PROSPACE SCHEMATIC FILE
; Version 2024.4.0
; Codepage=1252

The version changes from file to file. Unreal Engine animation files also use the .psa extension. This package does not read them.

Installation

Install the package and Effect from npm:

npm install @vazen/toml effect

The package needs Node.js >=24 and ES modules. Effect ^4.0.0 is a peer dependency. A Python package is planned.

The examples on this page use @vazen/toml 0.4.0 and Node.js 24.

Convert PSA to JSON

Download the made-up file v2024.4.0.psa (opens in a new tab) and save it as example.psa, or run:

curl -o example.psa https://raw.githubusercontent.com/vazen/toml/main/typescript/src/psa/.test-data/v2024.4.0.psa

Save this script as convert-to-json.mjs beside the file. Then run node convert-to-json.mjs.

import { readFileSync, writeFileSync } from 'node:fs';
import {
  decodeVazenProjectFromPsaFile,
  encodeJsonFileFromVazenProject,
} from '@vazen/toml';

const { project } = decodeVazenProjectFromPsaFile(readFileSync('example.psa'));
writeFileSync(
  'example.vazen.json',
  encodeJsonFileFromVazenProject({ project }),
);

The start of example.vazen.json is:

{
  "schema": "vazen/layout",
  "schema_version": "0.3.0",
  "name": "Fictional Project 5",
  "products": [
    {
      "ref": "p_18jm0l02ylwz1",
      "name": "Product 01",
      "gtin": "5000000000001",
      "dimensions": {
        "width": 65,
        "height": 120,
        "depth": 60
      },
      "_psa__id": "7000001",

Convert PSA to Vazen TOML

Save this script as convert-to-toml.mjs beside the file. Then run node convert-to-toml.mjs.

import { readFileSync, writeFileSync } from 'node:fs';
import {
  decodeVazenProjectFromPsaFile,
  encodeTomlFileFromVazenProject,
} from '@vazen/toml';

const { project, source, messages } = decodeVazenProjectFromPsaFile(
  readFileSync('example.psa'),
);
writeFileSync(
  'example.vazen.toml',
  encodeTomlFileFromVazenProject({ project }),
);
console.log(source);
// Output: { declaredVersion: '2024.4.0', format: 'psa' }
console.log(messages);
// Output: []

The start of example.vazen.toml is:

schema = "vazen/layout"
schema_version = "0.3.0"
name = "Fictional Project 5"
_psa__key = "PRJ-5"
_psa__primary_key = "id"
_psa__case_multiple = 1
_psa__days_supply = 7
_psa__number_of_stores = 12

The JSON and the TOML file hold the same tables:

  • Each planogram becomes a fixture.
  • Each fixture row becomes equipment in that fixture.
  • Each position becomes a site.
  • Every length is in millimetres.
  • A value with a standard key uses that key. Every other value becomes a _psa__ key on its table, unless the value is the column’s default.

The specification defines each table.

Convert from the command line

The package's vazen-toml command converts a file without a script, and never changes the file. This writes the same example.vazen.toml:

npx @vazen/toml@0.4.0 convert example.psa --to toml > example.vazen.toml

inspect lists the plans in a file, and --plan converts one of them. The package README (opens in a new tab) lists the command's diagnostics and exit codes.

Write a PSA file

encodePsaFileFromVazenProject writes a layout as a PSA file. This script reads example.vazen.toml and writes it as PSA:

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

const { project } = decodeVazenProjectFromTomlFile(
  readFileSync('example.vazen.toml', 'utf8'),
);
writeFileSync('example-copy.psa', encodePsaFileFromVazenProject({ project }));

The writer writes PSA 2024.4.0. The version option names another version that PsaVersion lists: 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.

const bytes = encodePsaFileFromVazenProject({ project, version: '2017.2.0' });

The writer refuses a value that a column of that version cannot hold. It writes every file in Windows-1252.

What a conversion keeps

The reader reads a file as UTF-8 if it is valid UTF-8, and as Windows-1252 if not. Reading makes a best effort and reports any lost values, uncertain interpretations or generated ids in messages, beside the project. An empty list means no messages were reported. Each entry identifies the affected record type, rows and column where known:

MessageMeaning
UnlistedWidthRows with a cell count that no listed version has for their type. They are read by position, so a value can be in the wrong column.
DroppedCellsValues in cells after the last column of a row. They are dropped.
UnreadCellsCells that their column cannot read. They are read as empty.
IgnoredRowsRows with no place in PsaData, such as a position before the first fixture of its planogram. They are ignored.
GeneratedIdsPlanograms without a key. Each gets an id from its place in the file, such as planogram-1, which writing puts in its key.

Read a PSA file, then write the project as PSA again. The project can pass through TOML or JSON between the two steps. For a file read with no messages, the new file holds the same values, with these differences:

  • A cell that holds its column’s default can become empty, and an empty cell can get the default. The value does not change.
  • Text is written in Unicode’s composed form, and U+FFFD is written as ?.
  • A length is rounded to nine decimal places of a millimetre.
  • The number format, line endings and row order can change.

So a round trip does not give the same bytes. The PSA README (opens in a new tab) gives every rule for reading and writing.

Edit PSA records

To edit PSA columns directly, read the records, not a project. decodePsaDataFromPsaFile returns PsaData, the rows of the file as records, and its messages. encodePsaFileFromPsaData writes the records as a file. The API overview lists every function.

Blue Yonder and .psa are trademarks of their respective owners. Vazen is not affiliated with or endorsed by Blue Yonder. PSA support was built from PSA files that customers supplied or that were published online; column and value names follow ProSpace’s field definitions. No Blue Yonder software was used.