Skip to content

fix(regex): allow } to be escaped - #1691

Merged
ssalbdivad merged 2 commits into
arktypeio:mainfrom
cpruijsen:fix/issue-1690-73ee8f54
Oct 20, 2026
Merged

ssalbdivad merged 2 commits into
arktypeio:mainfrom
cpruijsen:fix/issue-1690-73ee8f54

Conversation

@cpruijsen

Copy link
Copy Markdown

regex() now accepts \} and \/ instead of reporting them as unnecessary escapes. Fixes #1690.

With the u or v flag, an unescaped } is a SyntaxError (Lone quantifier brackets), so \} is the only way to match a literal }. Before this change, regex("\\{\\}", "u") failed to typecheck with Escape preceding } is unnecessary and should be removed.

parseEscapedChar (ark/regex/escape.ts) rejects any escaped char that is not in Control. Control in ark/regex/state.ts held every ECMAScript syntax character except }, so this adds }. Escapes inside a char class use the same function, so [\}] is fixed too.

It also adds /. \/ failed for the same reason and is a valid escape in every mode, including u and v. The issue names only }, so if you want / out, remove | "/".

The new escaped braces (with the "u" case from the issue) and escaped forward slash tests in ark/regex/__tests__/regex.test.ts fail on the base commit and pass with the change.

Not in this PR: regex("}", "u") still typechecks, but new RegExp("}", "u") throws. Catching that needs a flag-aware check for unescaped literals.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

  • \} accepted by Control — ark/regex/state.ts:95 adds "}" to the Control union, so parseEscapedChar (ark/regex/escape.ts:55) now returns } instead of writeUnnecessaryEscapeMessage. Correct: } is an ECMAScript SyntaxCharacter, and under u/v the escape is the only way to match it literally. The same function backs the char-class path (ark/regex/charset.ts:67), so [\}] is fixed too.
  • \/ accepted by Control — ark/regex/state.ts:98 adds "/". Valid as an identity escape in every mode (verified new RegExp("a\\/c", "u") and "v" both parse), so this is safe; see the note below on whether it belongs in the same set as the syntax characters.
  • New tests — escaped braces (including the u-flag case from the issue) and escaped forward slash in ark/regex/__tests__/regex.test.ts. I confirmed both fail on the base commit with Escape preceding } / is unnecessary and should be removed. and pass with the change, and that the existing escape/charset suite still passes (57 passing).

ℹ️ "/" is a deliberate scope expansion beyond issue #1690

The issue names only }, and / is not a regex syntax character — in a string-form RegExp an unescaped / is already literal, so \/ is never required, unlike every other member of Control. It is nonetheless a valid identity escape in all modes and eases porting regex literals (where \/ is mandatory) into regex(). This is a judgment call the author already flagged; keeping it is reasonable, but it should be an intentional exception to the "unnecessary escape" rule.

Technical details
# `"/"` in `Control` changes behavior without addressing a required escape

## Affected sites
- `ark/regex/state.ts:98` — `"/"` added to the `Control` union.

## Required outcome
- Decide whether `regex("a\\/b")` should typecheck. If yes, the exception is fine as-is. If no (to stay consistent with rejecting escapes that are never necessary in string form), remove `| "/"` and drop the `escaped forward slash` test.

## Notes
- Verified: `new RegExp("a\\/c", "")`, `"u"`, and `"v"` all parse — `\/` is valid everywhere.
- Contrast with `"-"` (already in `Control`): `new RegExp("a\\-c", "u")` throws `Invalid escape`, so `Control` already contains at least one escape that is only valid inside a char class. Fully flag-aware validation would need to distinguish those, which this PR (correctly) does not attempt.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

cpruijsen and others added 2 commits October 20, 2026 12:32
An unescaped / is already literal in a string pattern under every flag,
so \/ is never required, unlike }, which the u and v flags reject
unescaped (Lone quantifier brackets). The / in a regex literal only
needs escaping because it delimits the literal, which never applies to
regex()'s string input.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ssalbdivad
ssalbdivad force-pushed the fix/issue-1690-73ee8f54 branch from d861b85 to 8b509e9 Compare October 20, 2026 16:39
@ssalbdivad ssalbdivad changed the title fix(regex): allow } and / to be escaped fix(regex): allow } to be escaped Oct 20, 2026
@ssalbdivad
ssalbdivad merged commit 3092166 into arktypeio:main Oct 20, 2026
13 checks passed
@github-project-automation github-project-automation Bot moved this from To do to Done (merged or closed) in arktypeio Oct 20, 2026
@ssalbdivad

Copy link
Copy Markdown
Member

thanks @cpruijsen! merged with one tweak: dropped \/, since an unescaped / is already literal in a string pattern under every flag, so that escape is never needed (unlike } with u/v)

ssalbdivad added a commit that referenced this pull request Oct 20, 2026
Bumps all publishable packages and documents the changes merged since
2.2.8:

- keep tuple elements around defaults in .in and .out (#1696, @ybai08)
- reject NaN in unions discriminated by typeof (#1702, @thecotne)
- don't treat Object.prototype keys as declared (#1701, @thecotne)
- only emit referenced $defs in toJsonSchema (#1689, fixes #1612,
  #1511 and #1546, @giaBaoJS)
- convert unrepresentable types in ~standard.jsonSchema instead of
  throwing (#1694)
- arkregex: allow } to be escaped (#1691, @cpruijsen)
- attest: onlyNamedTestFiles (#1688)

Also moves attest to TypeScript 7.1's stable native API. Starting with
7.1.0-dev.20261009.1, typescript/unstable/{ast,sync,fs} are exported as
typescript/{ast,sync,fs}, so the published 0.57.0 fails on every current
7.1 build with "Package subpath './unstable/ast' is not defined by
exports". The tsconfig paths aliases for the old specifiers kept
resolving them, so typecheck stayed green while attest crashed at
runtime. They are renamed alongside the imports.

The workspace TypeScript moves to 7.1.0-dev.20261020.3, the latest
nightly (no 7.1 beta is published yet).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ssalbdivad added a commit that referenced this pull request Oct 20, 2026
Bumps all publishable packages and documents the changes merged since
2.2.8:

- keep tuple elements around defaults in .in and .out (#1696, @ybai08)
- reject NaN in unions discriminated by typeof (#1702, @thecotne)
- don't treat Object.prototype keys as declared (#1701, @thecotne)
- only emit referenced $defs in toJsonSchema (#1689, fixes #1612,
  #1511 and #1546, @giaBaoJS)
- convert unrepresentable types in ~standard.jsonSchema instead of
  throwing (#1694)
- arkregex: allow } to be escaped (#1691, @cpruijsen)
- attest: onlyNamedTestFiles (#1688)

Also moves attest to TypeScript 7.1's native API paths. Starting with
7.1.0-dev.20261009.1, typescript/unstable/{ast,sync,fs} are exported as
typescript/{ast,sync,fs}, so the published 0.57.0 fails on every current
7.1 build with "Package subpath './unstable/ast' is not defined by
exports". The tsconfig paths aliases for the old specifiers kept
resolving them, so typecheck stayed green while attest crashed at
runtime. They are renamed alongside the imports.

The workspace TypeScript moves to 7.1.0-beta, which exports the same
paths.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ssalbdivad added a commit that referenced this pull request Oct 20, 2026
Bumps all publishable packages and documents the changes merged since
2.2.8:

- keep tuple elements around defaults in .in and .out (#1696, @ybai08)
- reject NaN in unions discriminated by typeof (#1702, @thecotne)
- don't treat Object.prototype keys as declared (#1701, @thecotne)
- only emit referenced $defs in toJsonSchema (#1689, fixes #1612,
  #1511 and #1546, @giaBaoJS)
- convert unrepresentable types in ~standard.jsonSchema instead of
  throwing (#1694)
- arkregex: allow } to be escaped (#1691, @cpruijsen)
- attest: onlyNamedTestFiles (#1688)

Also moves attest to TypeScript 7.1's native API paths. Starting with
7.1.0-dev.20261009.1, typescript/unstable/{ast,sync,fs} are exported as
typescript/{ast,sync,fs}, so the published 0.57.0 fails on every current
7.1 build with "Package subpath './unstable/ast' is not defined by
exports". The tsconfig paths aliases for the old specifiers kept
resolving them, so typecheck stayed green while attest crashed at
runtime. They are renamed alongside the imports.

The workspace TypeScript moves to 7.1.0-beta, which exports the same
paths.
ssalbdivad added a commit that referenced this pull request Oct 21, 2026
* fix(regex): allow } and / to be escaped

* fix(regex): keep \/ an unnecessary escape

An unescaped / is already literal in a string pattern under every flag,
so \/ is never required, unlike }, which the u and v flags reject
unescaped (Lone quantifier brackets). The / in a regex literal only
needs escaping because it delimits the literal, which never applies to
regex()'s string input.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Cherry-picked from arktype 2.2.9 (3092166).

Co-authored-by: David Blass <david@arktype.io>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ssalbdivad added a commit that referenced this pull request Oct 21, 2026
main contributes the 2.2.9 release: version bumps for every publishable
package (arktype 2.2.9, @ark/schema, @ark/util and @ark/fs 0.56.8, attest
0.57.1, arkregex 0.0.14, @ark/json-schema 0.0.13, @ark/fast-check 0.0.20),
attest's move to TypeScript 7.1's native API paths (typescript/{ast,sync,fs}
in place of typescript/unstable/*, with the workspace catalog on
7.1.0-beta), arkdark's regex() highlighting and CI/icon changes, and the
2.2.9 fixes: tuple elements around defaults in .in/.out (#1696), the NaN
check in typeof-discriminated unions (#1702), Object.prototype keys as
undeclared (#1701), only referenced $defs in toJsonSchema (#1689) and
escaped } in arkregex (#1691).

featherduster already had an equivalent of every one of those fixes, each
with main's test or a broader one, so every code conflict keeps
featherduster's side:

- ark/schema constraint.ts, roots/{domain,root,union}.ts,
  structure/{sequence,structure}.ts, shared/toJsonSchema.ts: main patched
  code that featherduster has moved to ark/sets (intersectConstraints,
  discrimination, toJsonSchema) or rewritten. featherduster already narrows
  two constraints with one bound, reduces tuple prefix/optionals around
  defaultables, keeps the NaN check on a typeof case, uses a null-prototype
  propsByKey and resolves only referenced $defs.
- ark/type/__tests__/discrimination.test.ts: main's "number case includes
  NaN" is covered by featherduster's "number case includes NaN and
  Infinity". Its jitless loop is dropped, since testJitless runs the whole
  suite jitless.
- ark/type/__tests__/keywords/number.test.ts: featherduster's messages
  ("must be a number (was NaN)") are kept. A miss of number | null has the
  discriminant code there, so it names only the branch the value reached.
- ark/type/__tests__/match.test.ts: featherduster's caseReference snapshots.
- ark/type/__tests__/toJsonSchema.test.ts: featherduster's referencedDefs
  tests cover main's three useRefs cases (#1546, #1511, #1612).
- CHANGELOGs: featherduster already carried arktype's 2.2.9 and arkregex's
  0.0.14 sections, identical to main's, below its Unreleased sections. In
  attest's, main's 0.57.1 section goes below Unreleased.
- pnpm-lock.yaml: featherduster's, reinstalled against the merged
  catalog, so the only change is typescript 7.1.0-dev.20261006.1 ->
  7.1.0-beta.

The precompiled-module version link moves with the version: runtime.ts
exports precompiledForArktype_2_2_9 in place of precompiledForArktype_2_2_8,
which build.ts's checkVersionExport derives from package.json. The
precompile setup and troubleshooting docs, and llms-full.txt generated
from them, name 2_2_9 and "runs on 2.2.9" in their examples. The docs'
generated util.ts declarations pick up @ark/util 0.56.8.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ssalbdivad added a commit that referenced this pull request Oct 22, 2026
Its --tsconfig null dropped the repo's ark-ts customConditions, so
workspace packages resolved to their built out/ declarations. CI builds
first, but locally a stale build made the loose run fail on changes like
#1691's that hadn't been rebuilt. It now resolves source like every other
typed run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done (merged or closed)

Development

Successfully merging this pull request may close these issues.

arkregex: \} is rejected as an unnecessary escape, which is required by u and v flags

2 participants