@vazen-ai/toml 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,80 @@
1
+ # Changelog
2
+
3
+ Changes to the `@vazen-ai/toml` npm package. Package versions are independent of
4
+ the Vazen TOML file format's `schema_version`.
5
+
6
+ <!-- Each version repeats the Added, Changed and Fixed headings. -->
7
+ <!-- markdownlint-configure-file { "MD024": { "siblings_only": true } } -->
8
+
9
+ ## [Unreleased]
10
+
11
+ ## [0.2.1] - 2026-10-08
12
+
13
+ ### Fixed
14
+
15
+ - Fix the npm release workflow, which stopped before publishing 0.2.0. The
16
+ package code is unchanged from 0.2.0.
17
+
18
+ ## [0.2.0] - 2026-10-06
19
+
20
+ ### Added
21
+
22
+ - Read PSA tray, case, display and alternate merchandising styles as products
23
+ with their own packaging dimensions and unit counts.
24
+ - Return structured processing messages alongside the project and source from
25
+ file readers. PSA messages report recoverable problems; TOML and JSON readers
26
+ return an empty list. Writers do not store messages in converted files.
27
+ - Include this changelog in the npm package.
28
+
29
+ ### Changed
30
+
31
+ - Require Effect 4.0.0 or a later 4.x release as a peer dependency. Exported
32
+ schemas and errors use Effect 4 types, including `SchemaError` in place of
33
+ `ParseError`. Effect schema decoding uses `Result` instead of `Either`.
34
+ - Rename the PSA records API's `compromises` field to `messages`, and
35
+ `PsaCompromise` to `PsaMessage`.
36
+ - Reduce temporary allocations when reading PSA cells and writing PSA bytes.
37
+
38
+ ### Fixed
39
+
40
+ - Match PSA positions using the declared UPC, ID or combined primary key, and
41
+ preserve unused position identifiers through TOML and JSON round trips. Keep
42
+ separate PSA product rows distinct when writing.
43
+ - Keep reserved PSA text as text through JSON round trips. Distinguish TOML date
44
+ kinds when matching product selectors.
45
+ - Read integer PSA codes written as decimals, such as `0.00`.
46
+ - Refuse ambiguous Windows-1252 output that would read back as different UTF-8
47
+ text. Preserve Windows-1252 bytes `0x80` to `0x9F` on every supported Node.js
48
+ version.
49
+ - Reject `__proto__` keys when reading or writing TOML and JSON, instead of
50
+ allowing prototype changes or silently losing data.
51
+
52
+ ## [0.1.0] - 2026-09-29
53
+
54
+ `@vazen-ai/toml` 0.1.0 is the first release of the package. It reads and writes
55
+ [Vazen TOML](https://toml.vazen.com/) 0.3.0 files, `vazen/spec` and
56
+ `vazen/layout`, as TOML or as JSON. It reads ProSpace PSA files of any version,
57
+ and writes PSA 2017.1.0 to 2024.4.0. Each file reads as the tables of a 0.3.0
58
+ file. A writer writes 0.3.0, or PSA 2024.4.0, unless another version is asked
59
+ for.
60
+
61
+ ```sh
62
+ npm install @vazen-ai/toml
63
+ ```
64
+
65
+ ### What reading does not check
66
+
67
+ Reading checks the shape of a file. It does not check the specification's rules
68
+ of a whole file, nor its spatial rules. A project read from a PSA file drops
69
+ what the records reader lists as compromises, such as a cell its column could
70
+ not read; `decodePsaDataFromPsaFile` shows them.
71
+ [The package README](https://github.com/vazen-ai/toml/blob/main/typescript/README.md)
72
+ says what each function reads and writes, and
73
+ [the PSA README](https://github.com/vazen-ai/toml/blob/main/typescript/src/psa/README.md)
74
+ says which values the PSA reader loses and which the writer keeps.
75
+
76
+ ### Requirements
77
+
78
+ The package is ESM only, for Node.js 24 or later. It needs Effect 3.14.8 or a
79
+ later 3.x release as a peer dependency. This release was published by hand and
80
+ carries no provenance.
package/README.md CHANGED
@@ -1,20 +1,23 @@
1
1
  # @vazen-ai/toml
2
2
 
3
- `@vazen-ai/toml` 0.1.0 reads and writes [Vazen TOML](https://toml.vazen.com/)
4
- 0.3.0 files, `vazen/spec` and `vazen/layout`, as TOML or as JSON. It reads
5
- ProSpace PSA files of any version, and writes the versions `PsaVersion` lists,
6
- 2017.1.0 to 2024.4.0. Each file reads as the tables of a 0.3.0 file. A writer
7
- writes 0.3.0, or PSA 2024.4.0, unless another version is asked for.
3
+ `@vazen-ai/toml` reads and writes [Vazen TOML](https://toml.vazen.com/) 0.3.0
4
+ files, `vazen/spec` and `vazen/layout`, as TOML or as JSON. It reads ProSpace
5
+ PSA files of any version, and writes the versions `PsaVersion` lists, 2017.1.0
6
+ to 2024.4.0. Each file reads as the tables of a 0.3.0 file. A writer writes
7
+ 0.3.0, or PSA 2024.4.0, unless another version is asked for.
8
8
  [Reading and writing](#reading-and-writing) says what each function reads and
9
9
  writes, and what reading does not check.
10
10
 
11
+ The [changelog](CHANGELOG.md) records package changes separately from the file
12
+ format's versions.
13
+
11
14
  ```js
12
15
  import {
13
16
  decodeVazenProjectFromTomlFile,
14
17
  encodeJsonFileFromVazenProject,
15
18
  } from '@vazen-ai/toml';
16
19
 
17
- const { project, source } =
20
+ const { project, source, messages } =
18
21
  decodeVazenProjectFromTomlFile(`schema = "vazen/spec"
19
22
  schema_version = "0.3.0"
20
23
 
@@ -36,6 +39,8 @@ console.log(project.fixtures[0].equipment[0].sites[0].facings.wide);
36
39
  // 3
37
40
  console.log(source);
38
41
  // { declaredVersion: '0.3.0', format: 'vazen-toml' }
42
+ console.log(messages);
43
+ // []
39
44
  console.log(encodeJsonFileFromVazenProject({ project }).includes('"wide": 3'));
40
45
  // true
41
46
  ```
@@ -46,10 +51,16 @@ A project is the tables of a 0.3.0 file, `VazenTomlProjectV0_3_0`: `products`
46
51
  and `fixtures`, each fixture's `equipment`, and each piece of equipment's
47
52
  `sites`. When the file gives no value for a key, the key is absent. Keys the
48
53
  specification does not define sit beside the standard keys, as in the file. A
49
- reader returns the file's format and declared version as `source`, beside the
50
- project. The source is not part of the project, because a writer writes the
51
- project alone. Reading checks the shape of the file. It does not check the
52
- specification's rules of a whole file, nor its spatial rules.
54
+ reader returns `{ project, source, messages }`. The source gives the file's
55
+ format and declared version. The messages are structured findings from reading
56
+ or processing the project, with a `_tag` identifying each kind and details such
57
+ as affected records and columns. An empty list means none were reported. Writers
58
+ write the project alone, so these messages are not stored in converted files.
59
+
60
+ Readers return data that satisfies the project schema. This checks field types
61
+ and allowed values, not full planogram validity or spatial rules. The schemas
62
+ refuse an own `__proto__` key anywhere in a project, when reading or writing.
63
+ This keeps attribute keys safe to copy between JavaScript objects.
53
64
 
54
65
  ## JSON
55
66
 
@@ -57,18 +68,41 @@ The tables also read and write as JSON, in a `.vazen.json` file. The JSON has
57
68
  the same keys and the same values. JSON has no date, so the JSON writer writes a
58
69
  date as text, such as `"2026-09-27T09:30:00.000Z"`, `"2026-09-27"` or
59
70
  `"09:30:00.000"`. The JSON reader reads text in exactly that form as a date, but
60
- only in an attribute or a product selector. JSON cannot hold `nan` or `inf`, so
61
- the JSON writer refuses them. The JSON reader refuses an integer beyond
62
- JavaScript's safe range, as the TOML reader does.
71
+ only in an attribute or a product selector. Text under a `_psa__` key stays
72
+ text, since it holds a PSA cell, and so does a selector's text under a product's
73
+ standard key, such as `name`, since the product's is text. So a `label` of
74
+ `"2026-09-27"` and a `label` of the date `2026-09-27` both read back as the
75
+ date. A selector for the text then matches both products, and the PSA writer
76
+ refuses it. JSON cannot hold `nan` or `inf`, so the JSON writer refuses them.
77
+ The JSON reader refuses an integer beyond JavaScript's safe range, as the TOML
78
+ reader does.
63
79
 
64
80
  ## PSA files
65
81
 
66
82
  A PSA file reads as the tables of a 0.3.0 layout. Each planogram is a fixture,
67
- each fixture row is its equipment, and each position is a site. Every length is
68
- in millimetres. A value with a standard key takes that key. Every other value
69
- the file gives becomes a `_psa__` key on its object, unless the column's default
70
- already implies it. So the project writes back as the same PSA records, also
71
- after a trip through TOML or JSON.
83
+ each fixture row is its equipment, and each position is a site. A product that a
84
+ position places as a tray, a case, a display or an alternate also reads as one
85
+ more product for each of those styles, as the specification asks. Each one has
86
+ the style as its `form`, the style's dimensions, and the number of units it
87
+ holds as `_vazen__unit_count`. When written, they go back onto the same PSA
88
+ product row. Every length is in millimetres. A value with a standard key takes
89
+ that key. Every other value the file gives becomes a `_psa__` key on its object,
90
+ unless the column's default already implies it. With no reported messages, the
91
+ project writes back as the same PSA records, also after a trip through TOML or
92
+ JSON.
93
+
94
+ The PSA bridge matches products using the project's primary key: UPC alone, ID
95
+ alone, or the exact pair for Both. An absent primary-key cell defaults to UPC.
96
+ Identifier text is compared exactly, and a missing component compares as empty.
97
+ Both may have one empty component; each placed product and position must have at
98
+ least one identifier. Duplicate primary keys and positions with no match are
99
+ refused. There is no fallback to the secondary identifier.
100
+
101
+ When a position's unused identifier differs from its product's, its site keeps
102
+ the original cell in `_psa__id` or `_psa__upc`; an empty string means a blank
103
+ cell. These attributes survive TOML and JSON and write back to the position. An
104
+ export is refused if an edit makes them conflict with the selected product under
105
+ the current primary key.
72
106
 
73
107
  ```js
74
108
  import { readFileSync, writeFileSync } from 'node:fs';
@@ -86,6 +120,9 @@ writeFileSync(
86
120
  );
87
121
  ```
88
122
 
123
+ [Read and convert ProSpace PSA planogram files](https://toml.vazen.com/psa)
124
+ shows each conversion with a made-up PSA file to try.
125
+
89
126
  ## Reading and writing
90
127
 
91
128
  Each format has a pair of file functions. `decodeVazenProjectFromTomlFile` and
@@ -94,25 +131,25 @@ Each format has a pair of file functions. `decodeVazenProjectFromTomlFile` and
94
131
  write JSON text. `decodeVazenProjectFromPsaFile` and
95
132
  `encodePsaFileFromVazenProject` read and write PSA bytes. The TOML and JSON
96
133
  readers take the versions `VazenVersion` lists. The PSA reader takes a file of
97
- any version. A project read from a PSA file drops what the records reader lists
98
- in `compromises`, such as a cell its column could not read.
99
- `decodePsaDataFromPsaFile` shows them. The TOML and JSON writers write 0.3.0.
100
- The PSA writer writes 2024.4.0. The `version` option names another version that
101
- `VazenVersion` or `PsaVersion` lists:
134
+ any version on a best-effort basis. Recoverable problems, such as an unreadable
135
+ cell treated as empty or an unfamiliar row width, are reported in `messages`.
136
+ Reading fails when it cannot produce a project that satisfies the schema. The
137
+ TOML and JSON writers write 0.3.0. The PSA writer writes 2024.4.0. The `version`
138
+ option names another version that `VazenVersion` or `PsaVersion` lists:
102
139
  `encodePsaFileFromVazenProject({ project, version: '2017.2.0' })`. A writer
103
140
  refuses a project that a reader would refuse as a file. The PSA writer also
104
141
  refuses a value that a column of the named version cannot hold. Each function
105
- throws a `ParseError` saying what it could not read or write.
142
+ throws a `SchemaError` saying what it could not read or write.
106
143
 
107
144
  The Effect schemas behind the file functions are exported for a caller who wants
108
- an `Either` or an `Effect`: `VazenProjectFromTomlText`,
145
+ a `Result` or an `Effect`: `VazenProjectFromTomlText`,
109
146
  `VazenProjectFromJsonText`, `VazenProjectFromPsaBytes`,
110
147
  `VazenProjectFromPsaText` and `VazenProjectFromPsaData`. Each schema reads a
111
148
  file into `VazenProject`. In a `VazenProject`, an optional value is an Effect
112
149
  `Option`, the keys the specification does not define live in each object's
113
- `attributes`, and `source` is part of the project. So the PSA schemas write the
114
- version of the PSA file the project came from, when that version can hold the
115
- project.
150
+ `attributes`, and `source` and `messages` are part of the project. So the PSA
151
+ schemas write the version of the PSA file the project came from, when that
152
+ version can hold the project.
116
153
 
117
154
  The functions above read a file as a project. To edit the columns of a PSA file,
118
155
  read its records instead. `decodePsaDataFromPsaFile` returns `PsaData`, the rows
@@ -121,7 +158,7 @@ file.
121
158
  [The PSA README](https://github.com/vazen-ai/toml/blob/main/typescript/src/psa/README.md)
122
159
  says which values the reader loses and which values the writer keeps.
123
160
 
124
- The package is ESM only, for Node.js 24 or later. It needs Effect 3.14.8 or a
125
- later 3.x release as a peer dependency. To work on the package, run `npm ci`
161
+ The package is ESM only, for Node.js 24 or later. It needs Effect 4.0.0 or a
162
+ later 4.x release as a peer dependency. To work on the package, run `npm ci`
126
163
  then `npm run verify` here. Licensing is in
127
164
  [LICENSING.md](https://github.com/vazen-ai/toml/blob/main/LICENSING.md).