Socket
Socket
Sign inDemoInstall

smol-toml

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

smol-toml - npm Package Compare versions

Comparing version 1.0.0 to 1.0.1

2

dist/date.d.ts

@@ -28,3 +28,2 @@ /*!

*/
export declare let DATE_TIME_RE: RegExp;
export default class TomlDate extends Date {

@@ -37,2 +36,3 @@ #private;

isTime(): boolean;
isValid(): boolean;
toISOString(): string;

@@ -39,0 +39,0 @@ static wrapAsOffsetDateTime(jsDate: Date, offset?: string): TomlDate;

@@ -28,7 +28,7 @@ /*!

*/
export let DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[Tt ]?(\d{2}:\d{2}:\d{2}(?:\.\d{3,})?)?(Z|[-+]\d{2}:\d{2})?$/;
let DATE_TIME_RE = /^(\d{4}-\d{2}-\d{2})?[T ]?(?:(\d{2}):\d{2}:\d{2}(?:\.\d+)?)?(Z|[-+]\d{2}:\d{2})?$/i;
export default class TomlDate extends Date {
#hasDate;
#hasTime;
#offset;
#hasDate = false;
#hasTime = false;
#offset = null;
constructor(date) {

@@ -46,12 +46,17 @@ let hasDate = true;

hasTime = !!match[2];
offset = match[3] || null;
// Do not allow rollover hours
if (match[2] && +match[2] > 23) {
date = '';
}
else {
offset = match[3] || null;
date = date.toUpperCase();
}
}
else {
date = '';
}
}
super(date);
if (isNaN(this.getTime())) {
this.#hasDate = false;
this.#hasTime = false;
this.#offset = null;
}
else {
if (!isNaN(this.getTime())) {
this.#hasDate = hasDate;

@@ -66,3 +71,3 @@ this.#hasTime = hasTime;

isLocal() {
return !this.#hasTime || !this.#hasTime || !this.#offset;
return !this.#hasDate || !this.#hasTime || !this.#offset;
}

@@ -75,2 +80,5 @@ isDate() {

}
isValid() {
return this.#hasDate || this.#hasTime;
}
toISOString() {

@@ -77,0 +85,0 @@ let iso = super.toISOString();

@@ -29,53 +29,112 @@ /*!

import { parseKey } from './struct.js';
import { extractKeyValue } from './parse.js';
import { skipVoid, peekTable } from './util.js';
import { extractValue } from './parse.js';
import { skipVoid } from './util.js';
import TomlError from './error.js';
export { default as TomlDate } from './date.js';
function peekTable(key, table, meta, type) {
let t = table;
let m = meta;
let k;
let hasOwn = false;
let state;
for (let i = 0; i < key.length; i++) {
if (i) {
t = hasOwn ? t[k] : (t[k] = {});
m = (state = m[k]).c;
if (type === 0 /* Type.DOTTED */ && state.t === 1 /* Type.EXPLICIT */) {
return null;
}
if (state.t === 2 /* Type.ARRAY */) {
let l = t.length - 1;
t = t[l];
m = m[l].c;
}
}
k = key[i];
if ((hasOwn = Object.hasOwn(t, k)) && m[k]?.t === 0 /* Type.DOTTED */ && m[k]?.d) {
return null;
}
if (!hasOwn) {
if (k === '__proto__') {
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
Object.defineProperty(m, k, { enumerable: true, configurable: true, writable: true });
}
m[k] = {
t: i < key.length - 1 && type === 2 /* Type.ARRAY */
? 0 /* Type.DOTTED */
: type,
d: false,
i: 0,
c: {},
};
}
}
state = m[k];
if (state.t !== type) {
// Bad key type!
return null;
}
if (type === 2 /* Type.ARRAY */) {
if (!state.d) {
state.d = true;
t[k] = [];
}
t[k].push(t = {});
state.c[state.i++] = (state = { t: 1 /* Type.EXPLICIT */, d: false, i: 0, c: {} });
}
if (state.d) {
// Redefining a table!
return null;
}
state.d = true;
if (type === 1 /* Type.EXPLICIT */) {
t = hasOwn ? t[k] : (t[k] = {});
}
else if (type === 0 /* Type.DOTTED */ && hasOwn) {
return null;
}
return [k, t, state.c];
}
export function parse(toml) {
let res = {};
let meta = {};
let tbl = res;
let seenTables = new Set();
let seenValues = new Set();
let m = meta;
for (let ptr = skipVoid(toml, 0); ptr < toml.length;) {
if (toml[ptr] === '[') {
let isTableArray = toml[ptr + 1] === '[';
let end = toml.indexOf(']', ptr);
if (end === -1)
throw new TomlError('unfinished table encountered', {
let isTableArray = toml[++ptr] === '[';
let k = parseKey(toml, ptr += +isTableArray, ']');
if (isTableArray) {
if (toml[k[1] - 1] !== ']') {
throw new TomlError('expected end of table declaration', {
toml: toml,
ptr: k[1] - 1,
});
}
k[1]++;
}
let p = peekTable(k[0], res, meta, isTableArray ? 2 /* Type.ARRAY */ : 1 /* Type.EXPLICIT */);
if (!p) {
throw new TomlError('trying to redefine an already defined table or value', {
toml: toml,
ptr: ptr
ptr: ptr,
});
let k = parseKey(toml, ptr += +isTableArray + 1, end++);
let strKey = k.join('"."');
if (!isTableArray && seenTables.has(strKey))
throw new TomlError('trying to redefine an already defined table', {
toml: toml,
ptr: ptr - 1
});
seenTables.add(strKey);
let r = peekTable(res, k, seenValues, true);
if (!r) {
throw new TomlError('trying to redefine an already defined value', {
toml: toml,
ptr: ptr - 1
});
}
let v = r[1][r[0]];
if (!v) {
r[1][r[0]] = (v = isTableArray ? [] : {});
}
else if (isTableArray && !Array.isArray(v)) {
throw new TomlError('trying to define an array of tables, but a table already exists for this identifier', {
m = p[2];
tbl = p[1];
ptr = k[1];
}
else {
let k = parseKey(toml, ptr);
let p = peekTable(k[0], tbl, m, 0 /* Type.DOTTED */);
if (!p) {
throw new TomlError('trying to redefine an already defined table or value', {
toml: toml,
ptr: ptr - 2
ptr: ptr,
});
}
tbl = v;
if (isTableArray)
v.push(tbl = {});
ptr = end + +isTableArray;
let v = extractValue(toml, k[1]);
p[1][p[0]] = v[0];
ptr = v[1];
}
else {
ptr = extractKeyValue(toml, ptr, tbl, seenValues);
}
ptr = skipVoid(toml, ptr, true);

@@ -82,0 +141,0 @@ if (toml[ptr] && toml[ptr] !== '\n' && toml[ptr] !== '\r') {

@@ -30,2 +30,1 @@ /*!

export declare function extractValue(str: string, ptr: number, end?: string): [TomlPrimitive, number];
export declare function extractKeyValue(str: string, ptr: number, table: Record<string, TomlPrimitive>, seen: Set<any>, isInline?: boolean): number;

@@ -29,28 +29,11 @@ /*!

import { parseString, parseValue } from './primitive.js';
import { parseKey, parseArray, parseInlineTable } from './struct.js';
import { peekTable, indexOfNewline, skipUntil, skipComment, skipVoid } from './util.js';
import { parseArray, parseInlineTable } from './struct.js';
import { indexOfNewline, skipVoid, skipUntil, skipComment, getStringEnd } from './util.js';
import TomlError from './error.js';
function getStringEnd(str, seek) {
let first = str[seek];
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2]
? str.slice(seek, seek + 3)
: first;
seek += target.length - 1;
do
seek = str.indexOf(target, ++seek);
while (seek > -1 && first !== "'" && str[seek - 1] === '\\' && str[seek - 2] !== '\\');
seek += target.length;
if (target.length > 1) {
if (str[seek] === first)
seek++;
if (str[seek] === first)
seek++;
}
return seek;
}
function sliceAndTrimEndOf(str, startPtr, endPtr, allowNewLines) {
let value = str.slice(startPtr, endPtr);
let newlineIdx;
let commentIdx = value.indexOf('#');
if (commentIdx > -1) {
// The call to skipComment allows to "validate" the comment
// (absence of control characters)
skipComment(str, commentIdx);

@@ -61,7 +44,4 @@ value = value.slice(0, commentIdx);

if (!allowNewLines) {
let s = '\n';
newlineIdx = value.lastIndexOf('\n');
if (newlineIdx < 0)
newlineIdx = value.lastIndexOf(s = '\r');
if (trimmed.lastIndexOf(s) !== newlineIdx) {
let newlineIdx = value.indexOf('\n', trimmed.length);
if (newlineIdx > -1) {
throw new TomlError('newlines are not allowed in inline tables', {

@@ -73,3 +53,3 @@ toml: str,

}
return trimmed;
return [trimmed, commentIdx];
}

@@ -82,3 +62,3 @@ export function extractValue(str, ptr, end) {

: parseInlineTable(str, ptr);
let newPtr = skipUntil(str, endPtr, end);
let newPtr = skipUntil(str, endPtr, ',', end);
if (end === '}') {

@@ -100,5 +80,5 @@ let nextNewLine = indexOfNewline(str, endPtr, newPtr);

}
endPtr = skipUntil(str, ptr, end);
let valStr = sliceAndTrimEndOf(str, ptr, endPtr - (+(str[endPtr - 1] === ',')), end === ']');
if (!valStr) {
endPtr = skipUntil(str, ptr, ',', end);
let slice = sliceAndTrimEndOf(str, ptr, endPtr - (+(str[endPtr - 1] === ',')), end === ']');
if (!slice[0]) {
throw new TomlError('incomplete key-value declaration: no value specified', {

@@ -109,35 +89,10 @@ toml: str,

}
if (end && slice[1] > -1) {
endPtr = skipVoid(str, ptr + slice[1]);
endPtr += +(str[endPtr] === ',');
}
return [
parseValue(valStr, str, ptr),
endPtr
parseValue(slice[0], str, ptr),
endPtr,
];
}
export function extractKeyValue(str, ptr, table, seen, isInline) {
let equalIdx = str.indexOf('=', ptr);
if (equalIdx < 0) {
throw new TomlError('incomplete key-value declaration: no equals sign after the key', {
toml: str,
ptr: ptr
});
}
// KEY
let t = peekTable(table, parseKey(str, ptr, equalIdx), seen);
if (!t) {
throw new TomlError('trying to redefine an already defined value', {
toml: str,
ptr: ptr
});
}
// VALUE
ptr = skipVoid(str, equalIdx + 1, true, true);
if (str[ptr] === '\n' || str[ptr] === '\r') {
throw new TomlError('newlines are not allowed in key-value declarations', {
toml: str,
ptr: ptr
});
}
let e = extractValue(str, ptr, isInline ? '}' : void 0);
t[1][t[0]] = e[0];
seen.add(e[0]);
return e[1];
}

@@ -29,7 +29,7 @@ /*!

import { skipVoid } from './util.js';
import TomlDate, { DATE_TIME_RE } from './date.js';
import TomlDate from './date.js';
import TomlError from './error.js';
let NUM_REGEX = /^(0x|0b|0o|[+-])?[0-9a-f_]+(\.[0-9a-f_]+)?([e][+-]?[0-9a-f_]+)?$/i;
let INT_REGEX = /^(0x|0b|0o|[+-])?[0-9a-f]+$/;
let LEADING_ZERO = /^[+-]?0\d/;
let INT_REGEX = /^((0x[0-9a-fA-F](_?[0-9a-fA-F])*)|(([+-]|0[ob])?\d(_?\d)*))$/;
let FLOAT_REGEX = /^[+-]?\d(_?\d)*(\.\d(_?\d)*)?([eE][+-]?\d(_?\d)*)?$/;
let LEADING_ZERO = /^[+-]?0[0-9_]/;
let ESCAPE_REGEX = /^[0-9a-f]{4,8}$/i;

@@ -45,13 +45,2 @@ let ESC_MAP = {

};
let VALUES_MAP = {
true: true,
false: false,
inf: Infinity,
'+inf': Infinity,
'-inf': -Infinity,
nan: NaN,
'+nan': NaN,
'-nan': NaN,
'-0': 0,
};
export function parseString(str, ptr = 0, endPtr = str.length) {

@@ -73,9 +62,11 @@ let isLiteral = str[ptr] === "'";

let c = str[ptr++];
if (!isMultiline && (c === '\n' || c === '\r')) {
throw new TomlError('newlines are not allowed in strings', {
toml: str,
ptr: ptr - 1
});
if (c === '\n' || (c === '\r' && str[ptr] === '\n')) {
if (!isMultiline) {
throw new TomlError('newlines are not allowed in strings', {
toml: str,
ptr: ptr - 1
});
}
}
if (c < '\t' || c === '\x0b' || c === '\x0c' || c === '\x7f' || (c > '\x0d' && c < '\x20')) {
else if ((c < '\x20' && c !== '\t') || c === '\x7f') {
throw new TomlError('control characters are not allowed in strings', {

@@ -139,6 +130,18 @@ toml: str,

export function parseValue(value, toml, ptr) {
if (Object.hasOwn(VALUES_MAP, value))
return VALUES_MAP[value];
// Constant values
if (value === 'true')
return true;
if (value === 'false')
return false;
if (value === '-inf')
return -Infinity;
if (value === 'inf' || value === '+inf')
return Infinity;
if (value === 'nan' || value === '+nan' || value === '-nan')
return NaN;
// Numbers
if (NUM_REGEX.test(value)) {
let isInt;
if (value === '-0')
return 0; // Avoid FP representation of -0
if ((isInt = INT_REGEX.test(value)) || FLOAT_REGEX.test(value)) {
if (LEADING_ZERO.test(value)) {

@@ -150,6 +153,5 @@ throw new TomlError('leading zeroes are not allowed', {

}
value = value.replace(/_/g, '');
let numeric = +value;
if (INT_REGEX.test(value) && !Number.isSafeInteger(numeric)) {
throw new TomlError('integer value cannot be represented losslessly', {
let numeric = +(value.replace(/_/g, ''));
if (isNaN(numeric)) {
throw new TomlError('invalid number', {
toml: toml,

@@ -159,9 +161,4 @@ ptr: ptr

}
return numeric;
}
// Date
if (DATE_TIME_RE.test(value)) {
let date = new TomlDate(value);
if (isNaN(date.getTime())) {
throw new TomlError('invalid date', {
if (isInt && !Number.isSafeInteger(numeric)) {
throw new TomlError('integer value cannot be represented losslessly', {
toml: toml,

@@ -171,8 +168,12 @@ ptr: ptr

}
return date;
return numeric;
}
throw new TomlError('invalid value', {
toml: toml,
ptr: ptr
});
let date = new TomlDate(value);
if (!date.isValid()) {
throw new TomlError('invalid value', {
toml: toml,
ptr: ptr
});
}
return date;
}

@@ -29,4 +29,4 @@ /*!

import { type TomlPrimitive } from './util.js';
export declare function parseKey(str: string, startPtr?: number, endPtr?: number): string[];
export declare function parseKey(str: string, ptr: number, end?: string): [string[], number];
export declare function parseInlineTable(str: string, ptr: number): [Record<string, TomlPrimitive>, number];
export declare function parseArray(str: string, ptr: number): [TomlPrimitive[], number];

@@ -29,13 +29,18 @@ /*!

import { parseString } from './primitive.js';
import { extractValue, extractKeyValue } from './parse.js';
import { indexOfNewline, skipComment } from './util.js';
import { extractValue } from './parse.js';
import { skipComment, indexOfNewline, getStringEnd, skipVoid } from './util.js';
import TomlError from './error.js';
let KEY_PART_RE = /^[a-zA-Z0-9-_ \t]+$/;
export function parseKey(str, startPtr = 0, endPtr = str.length) {
let ptr;
let dot = -1;
let KEY_PART_RE = /^[a-zA-Z0-9-_]+[ \t]*$/;
export function parseKey(str, ptr, end = '=') {
let dot = ptr - 1;
let parsed = [];
let key = str.slice(startPtr, endPtr);
let endPtr = str.indexOf(end, ptr);
if (endPtr < 0) {
throw new TomlError('incomplete key-value: cannot find end of key', {
toml: str,
ptr: ptr
});
}
do {
let c = key[ptr = ++dot];
let c = str[ptr = ++dot];
// If it's whitespace, ignore

@@ -45,34 +50,49 @@ if (c !== ' ' && c !== '\t') {

if (c === '"' || c === "'") {
let eos = key.indexOf(c, ptr + 1) + 1;
if (!eos) {
if (c === str[ptr + 1] && c === str[ptr + 2]) {
throw new TomlError('multiline strings are not allowed in keys', {
toml: str,
ptr: ptr,
});
}
let eos = getStringEnd(str, ptr);
if (eos < 0) {
throw new TomlError('unfinished string encountered', {
toml: str,
ptr: ptr
ptr: ptr,
});
}
dot = key.indexOf('.', eos);
let end = key.slice(eos, dot < 0 ? void 0 : dot);
let newLine = indexOfNewline(end);
dot = str.indexOf('.', eos);
let strEnd = str.slice(eos, dot < 0 || dot > endPtr ? endPtr : dot);
let newLine = indexOfNewline(strEnd);
if (newLine > -1) {
throw new TomlError('newlines are not allowed in keys', {
toml: str,
ptr: ptr + dot + newLine
ptr: ptr + dot + newLine,
});
}
if (end.trimStart()) {
if (strEnd.trimStart()) {
throw new TomlError('found extra tokens after the string part', {
toml: str,
ptr: eos
ptr: eos,
});
}
parsed.push(parseString(key, ptr, eos));
if (endPtr < eos) {
endPtr = str.indexOf(end, eos);
if (endPtr < 0) {
throw new TomlError('incomplete key-value: cannot find end of key', {
toml: str,
ptr: ptr,
});
}
}
parsed.push(parseString(str, ptr, eos));
}
else {
// Normal raw key part consumption and validation
dot = key.indexOf('.', ptr);
let part = key.slice(ptr, dot < 0 ? void 0 : dot);
dot = str.indexOf('.', ptr);
let part = str.slice(ptr, dot < 0 || dot > endPtr ? endPtr : dot);
if (!KEY_PART_RE.test(part)) {
throw new TomlError('only letter, numbers, dashes and underscores are allowed in keys', {
toml: str,
ptr: ptr
ptr: ptr,
});

@@ -84,4 +104,4 @@ }

// Until there's no more dot
} while (dot + 1);
return parsed;
} while (dot + 1 && dot < endPtr);
return [parsed, skipVoid(str, endPtr + 1, true, true)];
}

@@ -95,3 +115,3 @@ export function parseInlineTable(str, ptr) {

while ((c = str[ptr++]) !== '}' && c) {
if (c === '\n' || c === '\r') {
if (c === '\n') {
throw new TomlError('newlines are not allowed in inline tables', {

@@ -115,3 +135,30 @@ toml: str,

else if (c !== ' ' && c !== '\t') {
ptr = extractKeyValue(str, ptr - 1, res, seen, true);
let k;
let t = res;
let hasOwn = false;
let [key, keyEndPtr] = parseKey(str, ptr - 1);
for (let i = 0; i < key.length; i++) {
if (i)
t = hasOwn ? t[k] : (t[k] = {});
k = key[i];
if ((hasOwn = Object.hasOwn(t, k)) && (typeof t[k] !== 'object' || seen.has(t[k]))) {
throw new TomlError('trying to redefine an already defined value', {
toml: str,
ptr: ptr
});
}
if (!hasOwn && k === '__proto__') {
Object.defineProperty(t, k, { enumerable: true, configurable: true, writable: true });
}
}
if (hasOwn) {
throw new TomlError('trying to redefine an already defined value', {
toml: str,
ptr: ptr
});
}
let [value, valueEndPtr] = extractValue(str, keyEndPtr, '}');
seen.add(value);
t[k] = value;
ptr = valueEndPtr;
comma = str[ptr - 1] === ',' ? ptr - 1 : 0;

@@ -118,0 +165,0 @@ }

@@ -35,3 +35,3 @@ /*!

export declare function skipVoid(str: string, ptr: number, banNewLines?: boolean, banComments?: boolean): number;
export declare function skipUntil(str: string, ptr: number, end?: string): number;
export declare function peekTable(table: Record<string, any>, key: string[], seen: Set<any>, allowSuper?: boolean): [string, Record<string, any>] | null;
export declare function skipUntil(str: string, ptr: number, sep: string, end?: string): number;
export declare function getStringEnd(str: string, seek: number): number;

@@ -28,10 +28,8 @@ /*!

*/
import TomlDate from './date.js';
import TomlError from './error.js';
export function indexOfNewline(str, start = 0, end = str.length) {
for (let i = start; i < end; i++) {
if (str[i] === '\n' || str[i] === '\r')
return i;
}
return -1;
let idx = str.indexOf('\n', start);
if (str[idx - 1] === '\r')
idx--;
return idx <= end ? idx : -1;
}

@@ -41,4 +39,8 @@ export function skipComment(str, ptr) {

let c = str[i];
if (c < '\t' || c === '\x0b' || c === '\x0c' || c === '\x7f' || (c > '\x0d' && c < '\x20')) {
throw new TomlError('control characters are not allowed in commebts', {
if (c === '\n')
return i;
if (c === '\r' && str[i + 1] === '\n')
return i + 1;
if ((c < '\x20' && c !== '\t') || c === '\x7f') {
throw new TomlError('control characters are not allowed in comments', {
toml: str,

@@ -48,17 +50,14 @@ ptr: ptr,

}
if (c === '\n' || c === '\r')
return i;
}
return -1;
return str.length;
}
export function skipVoid(str, ptr, banNewLines, banComments) {
let c;
while ((c = str[ptr]) === ' ' || c === '\t' || (!banNewLines && (c === '\n' || c === '\r')))
while ((c = str[ptr]) === ' ' || c === '\t' || (!banNewLines && (c === '\n' || c === '\r' && str[ptr + 1] === '\n')))
ptr++;
if (banComments || c !== '#')
return ptr;
ptr = skipComment(str, ptr);
return ptr < 0 ? str.length : skipVoid(str, ptr, banNewLines);
return banComments || c !== '#'
? ptr
: skipVoid(str, skipComment(str, ptr), banNewLines);
}
export function skipUntil(str, ptr, end) {
export function skipUntil(str, ptr, sep, end) {
if (!end) {

@@ -68,43 +67,38 @@ ptr = indexOfNewline(str, ptr);

}
let nextEnd = str.indexOf(end, ptr);
if (nextEnd < 0) {
// TODO: point to start of structure instead?
throw new TomlError('cannot find end of structure', {
toml: str,
ptr: ptr
});
for (let i = ptr; i < str.length; i++) {
let c = str[i];
if (c === '#') {
i = indexOfNewline(str, i);
}
else if (c === sep) {
return i + 1;
}
else if (c === end) {
return i;
}
}
let nextSep = str.indexOf(',', ptr) + 1;
return !nextSep || nextEnd < nextSep ? nextEnd : nextSep;
throw new TomlError('cannot find end of structure', {
toml: str,
ptr: ptr
});
}
let DESCRIPTOR = { enumerable: true, configurable: true, writable: true };
export function peekTable(table, key, seen, allowSuper) {
let k = '';
let v;
let hasOwn;
let hadOwn;
for (let i = 0; i < key.length; i++) {
if (i) {
if (!(hadOwn = hasOwn)) {
if (k === '__proto__')
Object.defineProperty(table, k, DESCRIPTOR);
table[k] = {};
}
table = table[k];
if (Array.isArray(table))
table = table[table.length - 1];
export function getStringEnd(str, seek) {
let first = str[seek];
let target = first === str[seek + 1] && str[seek + 1] === str[seek + 2]
? str.slice(seek, seek + 3)
: first;
seek += target.length - 1;
do
seek = str.indexOf(target, ++seek);
while (seek > -1 && first !== "'" && str[seek - 1] === '\\' && str[seek - 2] !== '\\');
if (seek > -1) {
seek += target.length;
if (target.length > 1) {
if (str[seek] === first)
seek++;
if (str[seek] === first)
seek++;
}
k = key[i];
hasOwn = Object.hasOwn(table, k);
v = hasOwn ? table[k] : void 0;
if (v !== void 0 && (typeof v !== 'object' || seen.has(v))) {
return null;
}
}
if (hasOwn && (!allowSuper || (hadOwn && !Array.isArray(v)))) {
return null;
}
if (!hasOwn && k === '__proto__')
Object.defineProperty(table, k, DESCRIPTOR);
return [k, table];
return seek;
}
{
"name": "smol-toml",
"version": "1.0.0",
"version": "1.0.1",
"keywords": [

@@ -15,3 +15,3 @@ "toml",

"node": ">= 18",
"pnpm": ">= 7.24"
"pnpm": ">= 8"
},

@@ -18,0 +18,0 @@ "scripts": {

# smol-toml
[![TOML 1.0.0](https://img.shields.io/badge/TOML-1.0.0-9c4221?style=flat-square)](https://toml.io/en/v1.0.0)
[![License](https://img.shields.io/github/license/squirrelchat/smol-toml.svg?style=flat-square)](https://github.com/squirrelchat/smol-toml/blob/mistress/LICENSE)
[![npm](https://img.shields.io/npm/v/smol-toml?style=flat-square)](https://npm.im/smol-toml)
A small, fast, and correct TOML parser. smol-toml is fully spec-compliant with TOML v1.0.0.
A small, fast, and correct TOML parser. smol-toml is fully(ish) spec-compliant with TOML v1.0.0.

@@ -13,4 +14,28 @@ Why yet another TOML parser? Well, the ecosystem of TOML parsers in JavaScript is quite underwhelming, most likely due

smol-toml produces valid results (or errors) for all the test TOML files in https://github.com/iarna/toml-spec-tests.
smol-toml passes most of the tests from [BurntSushi's `toml-test` suite](https://github.com/BurntSushi/toml-test).
However, due to the nature of JavaScript and the limits of the language, it doesn't pass certain tests, namely:
- Invalid UTF-8 strings are not rejected
- Certain invalid UTF-8 codepoints are not rejected
- smol-toml doesn't preserve type information between integers and floats (in JS, everything is a float)
- smol-toml doesn't support the whole 64-bit range for integers (but does throw an appropriate error)
- As all numbers are floats in JS, the safe range is `2**53 - 1` <=> `-(2**53 - 1)`.
smol-toml also passes all of the tests in https://github.com/iarna/toml-spec-tests.
<details>
<summary>List of failed `toml-test` cases</summary>
These tests were done by modifying `primitive.ts` and make the implementation return bigints for integers. This allows
verifying the parser correctly intents a number to be an integer or a float.
*Ideally, this becomes an option of the library, but for now...*
The following tests are failing:
- invalid/encoding/bad-utf8-in-comment
- invalid/encoding/bad-utf8-in-multiline-literal
- invalid/encoding/bad-utf8-in-multiline
- invalid/encoding/bad-utf8-in-string-literal
- invalid/encoding/bad-utf8-in-string
- invalid/string/bad-codepoint
</details>
## Installation

@@ -42,4 +67,4 @@ ```

|----------------|---------------------|-------------------|----------------|----------------|
| Spec example | **60,733.91 op/s** | 32,565.20 op/s | 16,781.03 op/s | 31,336.67 op/s |
| ~5MB test file | **4.2567 op/s** | *DNF* | 2.4873 op/s | 2.5790 op/s |
| Spec example | **71,356.51 op/s** | 33,629.31 op/s | 16,433.86 op/s | 29,421.60 op/s |
| ~5MB test file | **3.8091 op/s** | *DNF* | 2.4369 op/s | 2.6078 op/s |

@@ -49,3 +74,3 @@ <details>

Tests ran using Vitest v0.31.0 on commit 361089f3dbc30d994494bf6ec1e8e2f135531247
Tests ran using Vitest v0.31.0 on commit 04d233e351f9ae719222154ee2217aea8b95dbab

@@ -57,13 +82,13 @@ CPU: Intel Core i7 7700K (4.2GHz)

✓ bench/parseSpecExample.bench.ts (4) 2466ms
✓ bench/parseSpecExample.bench.ts (4) 2462ms
name hz min max mean p75 p99 p995 p999 rme samples
· smol-toml 60,733.91 0.0145 0.2580 0.0165 0.0152 0.0319 0.0345 0.1383 ±0.46% 30367 fastest
· @iarna/toml 32,565.20 0.0268 0.3208 0.0307 0.0284 0.0580 0.0619 0.1699 ±0.54% 16283
· @ltd/j-toml 16,781.03 0.0505 1.0392 0.0596 0.0540 0.1147 0.1360 0.7657 ±1.52% 8391 slowest
· fast-toml 31,336.67 0.0298 0.3357 0.0319 0.0305 0.0578 0.0622 0.1580 ±0.41% 15669
✓ bench/parseLargeMixed.bench.ts (3) 15752ms
· smol-toml 71,356.51 0.0132 0.2633 0.0140 0.0137 0.0219 0.0266 0.1135 ±0.37% 35679 fastest
· @iarna/toml 33,629.31 0.0272 0.2629 0.0297 0.0287 0.0571 0.0650 0.1593 ±0.45% 16815
· @ltd/j-toml 16,433.86 0.0523 1.3088 0.0608 0.0550 0.1140 0.1525 0.7348 ±1.47% 8217 slowest
· fast-toml 29,421.60 0.0305 0.2995 0.0340 0.0312 0.0618 0.0640 0.1553 ±0.47% 14711
✓ bench/parseLargeMixed.bench.ts (3) 16062ms
name hz min max mean p75 p99 p995 p999 rme samples
· smol-toml 4.2567 225.85 257.42 234.92 242.74 257.42 257.42 257.42 ±3.35% 10 fastest
· @ltd/j-toml 2.4873 382.66 441.12 402.05 416.25 441.12 441.12 441.12 ±3.40% 10 slowest
· fast-toml 2.5790 377.86 409.32 387.75 392.90 409.32 409.32 409.32 ±2.07% 10
· smol-toml 3.8091 239.60 287.30 262.53 274.17 287.30 287.30 287.30 ±3.66% 10 fastest
· @ltd/j-toml 2.4369 376.73 493.49 410.35 442.58 493.49 493.49 493.49 ±7.08% 10 slowest
· fast-toml 2.6078 373.88 412.79 383.47 388.62 412.79 412.79 412.79 ±2.72% 10

@@ -74,10 +99,24 @@

smol-toml - bench/parseLargeMixed.bench.ts >
1.65x faster than fast-toml
1.71x faster than @ltd/j-toml
1.46x faster than fast-toml
1.56x faster than @ltd/j-toml
smol-toml - bench/parseSpecExample.bench.ts >
1.86x faster than @iarna/toml
1.94x faster than fast-toml
3.62x faster than @ltd/j-toml
2.12x faster than @iarna/toml
2.43x faster than fast-toml
4.34x faster than @ltd/j-toml
```
---
Additional notes:
I initially tried to benchmark `toml-nodejs`, but the 0.3.0 package is broken.
I initially reported this to the library author, but the author decided to
- a) advise to use a custom loader (via *experimental* flag) to circumvent the invalid imports.
- Said flag, `--experimental-specifier-resolution`, has been removed in Node v20.
- b) [delete the issue](https://github.com/huan231/toml-nodejs/issues/12) when pointed out links to the NodeJS
documentation about the flag removal and standard resolution algorithm.
For the reference anyways, `toml-nodejs` (with proper imports) is ~8x slower on both benchmark with:
- spec example: 7,543.47 op/s
- 5mb mixed: 0.7006 op/s
</details>
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc