Skip to content

Bound string construction with a maxStringLength option - #836

Closed
thiagojv wants to merge 1 commit into
jsonata-js:masterfrom
thiagojv:feat/max-string-length
Closed

Bound string construction with a maxStringLength option#836
thiagojv wants to merge 1 commit into
jsonata-js:masterfrom
thiagojv:feat/max-string-length

Conversation

@thiagojv

@thiagojv thiagojv commented Sep 2, 2026

Copy link
Copy Markdown

Closes #835.

$pad takes its width straight from the expression with no ceiling, so a ~25-byte expression allocates until the engine's own string limit stops it — around 536 million characters on V8. options.sequence does not see this: it works by replacing push on sequences from createSequence, and pad builds a raw Array and joins it.

const guarded = { timeout: 2000, stack: 500, sequence: 10000 };
await jsonata('$pad("", 20000000, "x")', guarded).evaluate({});
// before: a 20,000,000-character string, no error
// after:  D2016

Full measurements and the reasoning behind the default are in #835.

The change

options.maxStringLength, default 1e7, adjustable per evaluation. Errors with D2016.

The default is deliberately the same 1e7 the range operator already enforces — it is this library's existing answer to "how much may a single operation allocate", and pad is the operation that does not observe it. Being a library constant it is also identical on every engine, which an engine-derived default would not be, and jsonata-es5.js ships the same pad to browsers.

The check reads this.options exactly as append() already does for options.sequence, and it runs before the allocation rather than after.

Compatibility

This is a behaviour change. It lowers the effective ceiling from the engine maximum to 10 M, so a $pad between those bounds succeeds today and would start failing. The escape hatch is one line:

jsonata(expr, { maxStringLength: 536870888 })   // previous behaviour

We do not believe anyone pads to ten million characters deliberately, but that is a guess about your users rather than a compatibility argument. If you would rather ship the option opt-in first and tighten the default in a later major, that is a one-line change here and we are glad to make it.

Scope

Only pad — the one builtin taking an unbounded caller-supplied size. The same check is available to $replace, $join or $string if any is later found to amplify; we did not want to widen a first PR speculatively.

Tests

Six new cases under guardrails on string length, alongside the existing guardrails on sequence length: the default limit, a lowered limit, a raised limit, that the existing string counts toward the limit rather than only the padding added, and the two paths that must remain unaffected (padding within the limit, and a string needing no padding).

npm test  →  1805 passing
             statements 100 · branches 100 · functions 100 · lines 100

Coverage is unchanged at 100% across all four metrics, including the new branches. Lint clean. browserify, minify and build-es5 all run.

Signed-off-by: Thiago Junqueira thiagojv@gmail.com

🤖 Generated with Claude Code

$pad takes its width straight from the expression with no ceiling, so a ~25
byte expression allocates until the engine's own string limit stops it —
around 536 million characters on V8. options.sequence does not see this: it
works by replacing push on sequences from createSequence, and pad builds a raw
Array and joins it.

The new option defaults to 1e7, the same ceiling the range operator already
enforces, and can be raised or lowered per evaluation. Errors with D2016.

Signed-off-by: Thiago Junqueira <thiagojv@gmail.com>

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Turings98apprentice

Turings98apprentice commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

A fix for this $pad() issue already exists (#832). Please check the repo for existing issues (and PRs) before posting new ones.

@thiagojv thiagojv closed this Sep 2, 2026
@thiagojv
thiagojv deleted the feat/max-string-length branch September 2, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

options.sequence does not bound string construction — $pad allocates up to the engine's string limit

3 participants