Repository navigation
fix(regex): allow } to be escaped - #1691
Conversation
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
\}accepted byControl—ark/regex/state.ts:95adds"}"to theControlunion, soparseEscapedChar(ark/regex/escape.ts:55) now returns}instead ofwriteUnnecessaryEscapeMessage. Correct:}is an ECMAScriptSyntaxCharacter, and underu/vthe 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 byControl—ark/regex/state.ts:98adds"/". Valid as an identity escape in every mode (verifiednew 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 theu-flag case from the issue) andescaped forward slashinark/regex/__tests__/regex.test.ts. I confirmed both fail on the base commit withEscape 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.deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
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>
d861b85 to
8b509e9
Compare
|
thanks @cpruijsen! merged with one tweak: dropped |
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>
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>
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.
* 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>
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>
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>

regex()now accepts\}and\/instead of reporting them as unnecessary escapes. Fixes #1690.With the
uorvflag, an unescaped}is aSyntaxError(Lone quantifier brackets), so\}is the only way to match a literal}. Before this change,regex("\\{\\}", "u")failed to typecheck withEscape preceding } is unnecessary and should be removed.parseEscapedChar(ark/regex/escape.ts) rejects any escaped char that is not inControl.Controlinark/regex/state.tsheld 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, includinguandv. The issue names only}, so if you want/out, remove| "/".The new
escaped braces(with the"u"case from the issue) andescaped forward slashtests inark/regex/__tests__/regex.test.tsfail on the base commit and pass with the change.Not in this PR:
regex("}", "u")still typechecks, butnew RegExp("}", "u")throws. Catching that needs a flag-aware check for unescaped literals.