All notable changes to this project will be documented in this file.
This project adheres to Semantic Versioning.
The format of this changelog is a variant of Keep a Changelog.
New entries must be placed in a section entitled Unreleased
.
-
BREAKING CHANGES: require Node.js 20.0.0 or above
This allows us to use the built-in Node.js CLI parser and then to remove the Commander.js dependency. This reduces the standalone binary size from 77KB to 45KB (42%).
-
Support
require(esm)
in Node.js v22.10 and aboveThis package now has the new exports condition
module-sync
. This allows users of Node.js v22.10 and above to import the ESM version of the package usingrequire
. This avoids the issues of dual-package hazard. -
Remove
package.json
main
andmodule
fieldsThe
main
andmodule
fields supplemented by theexports
fields.exports
is supported since Node.js v12.7.0 Since we require Node.js v20.0.0 or above, we can safely removemain
.All major bundlers now support
exports
. Hence, we can also remove themodule
field.
-
BREAKING CHANGES: require Node.js 16.9.0 or above
-
BREAKING CHANGES: promote regular comments to doc-comments
Previously, bare-ts introduced a special syntax for doc-comments:
type Gender enum { ## Be inclusive :) FLUID MALE ## One is not born, but becomes a woman ## -- Simone de Beauvoir FEMALE } ## A Person with: ## - a name ## - a gender type Person { ## person's name name: str ## person's gender gender: optional<Gender> }
This syntax is not part of the BARE specification. Thus, the syntax is not portable between BARE_ implementations. To avoid this issue, bare-ts_ now uses regular comments as doc-comments. Every comment that precedes a type definition, an enum value, or a field is a doc-comment. The previous schema can now be written as follows:
type Gender enum { # Be inclusive :) FLUID MALE # One is not born, but becomes a woman # -- Simone de Beauvoir FEMALE } # A Person with: # - a name # - a gender type Person { # person's name name: str # person's gender gender: optional<Gender> }
-
BREAKING CHANGES: remove option
--import-config
Instead of importing a custom config, you can now pass the config through any encode function.
For instance, using the example of the README:
const payload = encodeContacts(contacts, { initialBufferLength: 256 /* bytes */, maxBufferLength: 512 /* bytes */, })
A default configuration is applied if no one is passed:
const payload = encodeContacts(contacts) // use the default config
-
BREAKING CHANGES: replace locations with offsets
Previously, every node and compiler errors carried a
loc
orlocation
property. A location object contained afilename
,line
,col
, andoffset
properties.The
filename
property is now contained in the AST root in the propertyfilename
.loc
andlocation
are replaced byoffset
.The line and column numbers must now be computed using the offset.
-
BREAKING CHANGES: enum member names in
PascalCase
instead ofCONSTANT_CASE
In a bare schema, an
enum
variant must be inCONSTANT_CASE
:type Status enum { OPEN = 0 CLOSE = 1 }
Previously, bare-ts preserved the case:
export enum Status { OPEN = "OPEN", CLOSE = "CLOSE", }
To follow the TypeScript convention, the case is now in
PascalCase
. Thus, bare-ts generates the following code:export enum Status { Open = "Open", Close = "Close", }
-
BREAKING CHANGES: remove option
--import-factory
Previously, bare-ts allowed external factory functions to build struct objects. For now, there is no replacement for this feature.
-
BREAKING CHANGES: remove option
--use-quoted-property
Previously, bare-ts allowed emitting JavaScript code with all object properties quoted. This feature was under-used and against the JavaScript conventions.
-
Fix name clashes
Previously, bare-ts did not support the use of aliases like
Map
orUint8Array
. Now, it properly handles these aliases and usesglobalThis
when necessary.
This release widely improves the usage of unions and flat unions.
-
BREAKING CHANGES: use strings tags for union of aliases
Now, bare-ts outputs string tags for unions of aliases. You can obtain the previous behavior using the option
--use-int-tag
.The following schema ...
type Person struct { name: str } type Organization struct { name: str } type Contact union { Person | Organization }
... generates the following types:
export type Person = { readonly name: string } export type Organization = { readonly name: string } export type Contact = | { tag: "Person"; val: Person } | { tag: "Organization"; val: Organization }
This makes code more readable and allows assignments between compatible unions.
Using the option
--use-int-tag
, you obtain the previous output:export type Person = { readonly name: string } export type Organization = { readonly name: string } export type Contact = | { tag: 0; val: Person } | { tag: 1; val: Organization }
-
BREAKING CHANGES: use type alias as tag's value for flat unions of structs
bare-ts allows flat unions of aliased structs. Previously, it used the type alias in
underscore_case
as tag's value. Now, it uses the type alias in its original case.For instance, the following union ...
type BoxedU32 struct { val: u32 } type BoxedStr struct { val: str } type Boxed union { BoxedU32 | BoxedStr }
... can be flatten (under
--use-flat-union
) to:export type BoxedU32 = { - readonly tag: "BOXED_U32", // Previous output + readonly tag: "BoxedU32", // New output readonly val: u32, } export type BoxedStr = { - readonly tag: "BOXED_STR", // Previous output + readonly tag: "BoxedStr", // New output readonly val: string, } export type Boxed = BoxedU32 | BoxedStr
-
BREAKING CHANGES: split
--use-flat-union
into--use-primitive-flat-union
and--use-struct-flat-union
Use
--use-primitive-flat-union
and--use-struct-flat-union
instead of--use-flat-union
. -
Flatten unions when possible under
--use-primitive-flat-union
and--use-struct-flat-union
bare-ts is able to flatten unions that consist of:
- basic types (bool, u8, str, ...) that have distinct
typeof
values - aliased structs
- (anonymous) structs
Previously,
use-flat-union
required that all unions could be flattened. This avoided the introduction of a "best-effort approach". However, this was too restrictive. A "best-effort approach" seems acceptable since it is opted in. Now, bare-ts attempts to flatten a union and falls back to a tagged union.Under
--use-struct-flat-union
, the following schema ...type A union { bool | f64 | str } type B union { f64 | i32 }
... compiles to the following types:
type A = boolean | number | string type B = { tag: 0; val: number } | { tag: 1; val: number }
Note that
B
is not flatten becausef64
andi32
have the sametypeof
value (number
).Under
--use-struct-flat-union
, the following schema ...type X struct { ... } type Y struct { ... } type XY union { X | Y } type Z Y type XZ union { X | Z } type Anonymous union { struct { ... } | struct { ... } }
... compiles to the following types:
type X = { tag: "X", ... } type Y = { tag: "Y", ... } type XY = X | Y type Z = Y type XZ = { tag: "X", val: X } | { tag: "Z", val: "Z" } type Anonymous = { tag: 0, ... } | { tag: 1, ... }
Note that the union
XZ
is not flatten, because one of the elements is not a struct or an aliased struct. Indeed,Z
is an aliased alias. - basic types (bool, u8, str, ...) that have distinct
-
Support flat unions of aliased structs and anonymous structs
Under the option
--use-struct-flat-union
, the following schema ...type Person struct { name: str } type Entity union { | Person # Anonymous entity | struct { name: str } }
... compiles to the following types
export type Person = { readonly tag: "Person" readonly name: string } export type Entity = | Person | { readonly tag: 1 readonly name: string }
We introduce this change for consistency purpose. You should avoid mixing aliased structs with anonymous structs
-
BREAKING CHANGES: emit TypeSCript's type aliases instead of interfaces
The following schema ...
type Person struct { name: str }
... compiles to a type alias instead of an interface:
- export interface Person { + export type Person = { readonly tag: "Person" readonly name: string }
-
BREAKING CHANGES: Emit ES2020
bare-ts now publishes ES2020 builds. This outputs smaller builds. This should cause no issue since we require a Node.js version
^14.18
or>=16
. -
Add option
--lib
to preventdecode
andencode
generationA decoder and an encoder are generated for every root type that doesn't resolve to
void
. The--lib
flag prevents this generation.This is particularly useful for libraries that export only readers and writers.
-
Allow root types that resolve to
void
Since the
0.9.0
version, root types that resolve tovoid
are forbidden.To conform with the BARE specification, they are now allowed. This makes valid the following schema:
type Root void
-
BREAKING CHANGES: Remove option
--use-lax-optional
This avoids breaking bijective encoding.
-
BREAKING CHANGES: Forbid flat unions of transitively aliased classes
Previously, bare-ts allowed flat unions of transitively aliased classes. It now rejects the following schema under the option
--use-flat-union
:type Named struct { name: str } type Person Named type Message union { Person }
-
BREAKING CHANGES: Require Node.js
>=14.18.0
This enables bare-ts to internally use
node:
prefixes for importing nodes' built-ins. -
Automatically discriminate aliased structs in flat unions
bare-ts is now able to add a discriminator field for aliased structs in flat union.
The name of the discriminator field is
tag
. For now, it is not possible to flatten aliased structs with at least one field namedtag
.Thus, under the option
--use-flat-union
, the following BARE schema ...type X struct { ... } type Y struct { ... } type XY union { X | Y }
... compiles to the following types:
export interface X { readonly tag: "X"; ... } export interface Y { readonly tag: "Y"; ... } export type XY = X | Y
-
Allow flat unions of anonymous structs
bare-ts now accepts flat unions of anonymous structs. It automatically uses the union tags to discriminate the structs.
Under the option
--use-flat-union
, the following BARE schema ...type XY union { struct { ... } | struct { ... } }
... compiles to the following types:
export type XY = { readonly tag: 0, ... } | { readonly tag: 1, ... }
-
BREAKING CHANGES: Rename
bare-ts
CLI tobare
-
BREAKING CHANGES: Rename
--legacy-syntax
to--legacy
-
BREAKING CHANGES: Remove options
--main
and--no-main
The previous version introduced automatic promotion of root type as main type. Root type aliases are type aliases that are not referred by a type in the schema. Main type aliases are types aliases used to decode and encode messages.
For the sake of simplicity, main type aliases and root types aliases are now identical.
In the following schema,
Post
is the only root and main type alias.type Person struct { name: str } type Post struct { author: Person }
-
BREAKING CHANGES: Forbid use-before-definition
In the last BARE draft, use-before-definition are disallowed. As a consequence, it also disallows recursive types. bare-ts now rejects the following schema:
type Y X type X u8
To enable this schema and recursive types, use the option
--legacy
. -
BREAKING CHANGES: Forbid root types that resolve to
void
The following schema is now invalid:
type Root void
This is not part of the BARE specification.
-
BREAKING CHANGES: Do not emit read/write for types resolving to void
-
Annotate your schema with doc-comments
A BARE doc-comment consists in two comment marks
##
. Doc-comments can only document:- type definitions
- enum values
- struct fields
The following schema documents these three kinds of object:
type Gender enum { ## Be inclusive :) FLUID MALE ## One is not born, but becomes a woman ## -- Simone de Beauvoir FEMALE } ## A Person with: ## - a name ## - a gender type Person { ## person's name name: str ## person's gender gender: optional<Gender> }
Note that this syntax is not part of the BARE specification. Thus, this is not portable between distinct implementations.
-
Add BARE code generator
This gives a basic way to format a schema. Note that comments (except doc comments) are stripped out.
bare-ts compile schema.bare -o schema.bare
bare-ts compile schema.bare --generator 'bare'
-
BREAKING CHANGES: Require @bare-ts/lib
v0.3.x
-
BREAKING CHANGES: Forbid
f32
andf64
as map key typeAccording to IEEE-754 2019: NaN (Not a Number) is not equal to any value, including itself.
This inequality leads to different implementations:
-
Implementations that "follows" the standard
An unbounded number of values may be bind to the key NaN and cannot be accessed. This is the implementation chosen by Golang.
-
Implementations that normalize NaNs and consider that NaN is equal to itself
This is the implementation chosen by JavaScript
-
Implementations that rely on the binary comparison of NaNs
These make complex the support of
f32
andf64
as map key type.To avoid this complexity, the ongoing BARE draft forbids their usage as map key type.
-
-
Automatically promote root type as main type
bare-ts generates encoders and decoders for main types. Main types can be selected with the option
--main
:bare-ts compile schema.bare --main Post
# schema.bare type Person struct { name: str } type Post struct { author: Person }
If the option
--main
is not set, then bare-ts promotes root types as main types. Root types are type aliases that are not referred by a type in the schema.In the previous schema,
Post
is a root type, whilePerson
is not. The following command has now the same effect as the previous one:bare-ts compile schema.bare
It promotes
Post
as a main type.To avoid the promotion of root types, you must use the option
--no-main
:bare-ts compile schema.bare --no-main
--no-main
and--main
cannot be both set. -
Allow leading and trailing pipes in unions
The following schema is now valid:
type LeadingPipe union { | u8 } type TrailingPipe union { u8 | }
-
Do not emit trailing spaces in code generation
-
BREAKING CHANGES: require Node.js versions that support ESM
bare-ts requires now a node versions that support ECMAScript Modules.
Note that, bare-ts still exports a CommonJS build.
-
Add pure annotations in generated code
Pure annotations enable bundler to detect pure function calls. bare-ts adds now these annotations in top-level function calls.
-
Allow circular references where possible
bare-ts was previously conservative about circular references. It now allows all circular references that can encode at least one finite message. It now accepts the following circular references:
type A list<A> type B map<str><B> type C list<optional<C>>[2] type D list<union { D | str }>[2] type E optional<E> type F union { F | str }
It still rejects the following circular references:
type X list<A>[2] type Y union { Y } type Z struct { field: Z }
-
BREAKING CHANGES: Update BARE syntax
bare-ts now supports the new syntax for BARE schema It reports legacy syntax as an error.
Use the option
--legacy-syntax
for allowing legacy syntax.
-
Forbid circular references with fixed lists
bare-ts now correctly rejects the following schema:
struct Person { bestFriends: [2]Person }
-
Allow enums and aliased types for map key type
The following schema is now valid:
enum Gender { FLUID MALE FEMALE } type GenderNames map[Gender] string
-
Allow unsorted tags for unions and enums
bare-ts now accepts the following schemas:
enum Gender { FLUID = 1 MALE = 0 ^ error was reported here FEMALE = 2 }
type UnsignedInt (u8 = 1 | u16 = 0 | u32 = 2 | u64 = 3) ^ error was reported here
Use the option
--pedantic
for rejecting these schemas.
-
BREAKING CHANGES: Forbid main codecs resolving to
void
The following BARE schema is no longer valid when 'Message' is a main codec:
type Message void ^ error is now reported here
-
BREAKING CHANGES: Forbid flat unions which cannot be automatically flatten
bare-ts is able to automatically compute the tag of simple flat unions without any help. A simple union is either:
- a union of base or void types that can be discriminated by their typeof value, or
- a union of classes (requires the option
--use-class
).
Previously, bare-ts asked the user to provide a tagging function for complex flat unions. Now, bare-ts throws an error when it encounters a complex flat union. Thus, it no longer supports complex flat unions.
-
Add pedantic mode (option
--pedantic
)The pedantic mode requires initializing enum tags and union tags.
-
Better code generation
bare-ts has a normalization step where it alias some types, including anonymous structs, data arrays, and typed arrays. bare-ts is now able to generate reader and writer without aliasing these types.
-
BREAKING CHANGES: Forbid BARE schema in which a union repeats a type
Now, bare-ts correctly rejects the following schema:
type X (u8 | u8)
-
BREAKING CHANGES: Default to
null
instead ofundefined
for optional typesThe use of
null
seems more common than the use ofundefined
.The option
--use-null
is removed. A new option--use-undefined
is added. -
Deduplicate readers and writers of complex non-aliased types
bare-ts generates readers and writers for complex non-aliased types. These readers and writers are now deduplicated.
-
Make configurable the emitted type for
void
BARE allows
void
types in unions. For example:type Union (u8 | void)
Previously, bare-ts emitted the type
undefined
forvoid
. Now, it relies on options--use-undefined
and--use-lax-optional
to choose betweennull
,undefined
, andnull | undefined
. Note that these options also modify the emitted types for optionals. -
Support for quoted properties
The option
--use-quoted-property
enables to output quoted properties instead of unquoted properties.This can be useful when using a minifier that differently handles quoted properties.
-
BREAKING CHANGES: Forbid BARE schema with undefined aliases
-
BREAKING CHANGES: Forbid BARE schema in which length and tags are too large
Length of fixed data and (typed) array must be an unsigned 32bits integer. This is a limitation of the ECMAScript standard.
Tags of enums and unions must be safe integers. In the future, this requirement could be relaxed by switching to bigint for larger integers.
-
BREAKING CHANGES: Forbid BARE schema in which the length of a fixed data is 0
The following schema is now invalid:
type EmptyData data<0>
-
BREAKING CHANGES: Forbid BARE schema in which a union repeats a type
The following schema is now invalid:
type X (u8 | u8)
Note that the following schema is still valid:
type Y u8 type X (u8 | Y)
Y
is a user-defined type. -
BREAKING CHANGES: Forbid enum members with the same name
The following schema is now invalid:
enum Gender { FLUID FEMALE MALE FLUID }
-
BREAKING CHANGES: Forbid struct that has several fields with the same name
The following schema is now invalid:
struct Person { name: string name: string }
-
BREAKING CHANGES: Forbid BARE schema with circular references
-
BREAKING CHANGES: adapt to @bare-ts/[email protected]
@bare-ts/[email protected] introduces several breaking changes. As a consequence:
- all decode/encode are renamed into read/write
- all pack/unpack are renamed into encode/decode
- decoders (previously unpackers) no longer accept
ArrayBuffer
as type of read buffer
-
Make bare-ts library platform-agnostic
Use your favorite ESM-ready CDN and simply import bare-ts. This was made possible by removing the dependency over
node:assert
. -
Add
--use-class
optionThis generates classes instead of interfaces for struct types.
-
Automatically handle simple flat unions
By default, bare-ts generates tagged unions. For instance, the following schema ...
type Union (A | B)
... compiles to the following types:
type Union = | { readonly tag: 0; readonly val: A } | { readonly tag: 1; readonly val: B }
You can force the use of flat unions with the option
--use-flat-union
. However, you have to provide a function that computes the tag of the object. This function must be exported from a file namedext.{js,ts}
and placed in the same directory as the file generated by bare-ts.export function tagUnion(x: A | B): 0 | 1 { // returns 0 if x has type A or 1 if x has type B }
bare-ts is now able to compute the tag of simple flat unions without your help. A simple union is either:
- a union of types that can be discriminated by their typeof value, or
- a union of classes (requires the option
--use-class
).
-
Add @bare-ts/lib as peer dependency
This informs the users of bare-ts which version of @bare-ts/lib to use.
-
Fix invalid code generation for big tags in enums and unions
bare-ts applies an optimization when tags can be encoded on 7 bits. It did not test the general case yet. The addition of tests enabled to catch typo errors. The generated code imported non-existing readers and writers.
-
Fix generator choice
The option
--generator
specifies which generator to use for generating the output. bare-ts usests
as default generator. The option--generator
should override this default.Previously, the option did not override the default.
-
Fix location report upon compilation errors
Upon errors, bare-ts reports the error and a file location. It previously reported the location at the end of the first involved token. It now reports the location at the start of the first involved token.
For instance, if a type alias is in lowercase in a BARE schema, then the parser reported an error at the end of the alias. It now reports the error at the start of the alias:
type lowerCaseAlias u8 ^ error was previously reported here ^ error is now reported here
-
Better diagnostics for reporting unwanted semicolons
-
Fix array encoders
Previously, array encoders did not encode the first item of a generic array.
- BARE schema compiler supports all types