Skip to content

fix(varlock): regex() takes flags, and say so when a pattern splits - #1087

Open
theoephraim wants to merge 5 commits into
mainfrom
parse-regex-literals-atomically
Open

theoephraim wants to merge 5 commits into
mainfrom
parse-regex-literals-atomically

Conversation

@theoephraim

@theoephraim theoephraim commented Sep 14, 2026

Copy link
Copy Markdown
Member

@type=string(matches=/^[0-9a-f]{7,40}$/) fails with "cannot mix positional args and named options". An unquoted /pattern/ is an ordinary value, not a token the grammar knows about, so the comma in the quantifier ends the value: the args become matches=/^[0-9a-f]{7 plus a positional 40}$/.

The fix leaves the parser alone. / stays an ordinary character, so no path is ever at risk of being read as a regex.

The answer for the user turns out to already exist: quote the literal whole, slashes and flags included.

# @type=string(matches="/^[0-9a-f]{7,40}$/i")
APP_COMMIT_SHA=

Regex detection runs on the value after the parser strips quotes, so quoting is pure transport -- it gets a ,, a space, or a ) past the value rules without changing what the value means. Nothing in the docs said so, and the error didn't either. Now both do.

Changes

  • The mixed-args error carries a tip when the arguments look like a pattern split by its own comma, naming the quoted-literal fix. It only ever adds a tip to a call that is already throwing, and is scoped to options that accept a regex, so a path-shaped fn(/usr/local/, /etc/) arrangement can't trip it.
  • regex("pattern", "flags") takes flags as a second argument. It was capped at one arg, so the spelling meant for awkward patterns couldn't carry flags at all.
  • A /.../ literal passed to regex() is now an error. It used to compile to a pattern matching the slashes themselves, silently. Rejected rather than stripped, since regex("/usr/lib/") could legitimately be a pattern for a path; the error names both fixes (drop the slashes, or escape them as \/).
  • d and v flags are recognized alongside the rest, in the runtime and in the VSCode diagnostics.
  • Docs state the real rule and drop two claims that were not true: that a quoted value is not detected as a regex, and that quoting a path-shaped match value in remap() makes it literal. They now also show how to match a slash-wrapped value exactly -- escape the inner slashes and anchor -- which is covered by a test.

Why not make /.../ a real token

That was the first version of this PR, and the grammar carried such a rule until #620 removed it for mis-lexing paths. The problem isn't fixable by a better rule: / is a path character at both ends of a path, so anything inferring "regex" from /.../ shape will sometimes capture paths. Delimiter-based lexing silently reads fn(/foo, bar/) -- a plausible two-path argument list -- as one regex, and narrower variants (allowing commas only inside {}) just make lexing depend on nesting.

Not fixed here

#1089, now narrowed to a papercut: a match value escaped without wrapping slashes ("\/usr\/lib\/") opts out of regex detection and is then compared against the raw escaped string, so it silently never matches. Everything is expressible today; that one shape just looks like it should work and doesn't.

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

bumpy-frog

The changes in this PR will be included in the next version bump.

minor Minor releases

  • @env-spec/parser 0.5.2 → 0.6.0
  • @varlock/native-helper-darwin 1.19.0 → 1.20.0
  • @varlock/native-helper-linux-arm64 1.19.0 → 1.20.0
  • @varlock/native-helper-linux-x64 1.19.0 → 1.20.0
  • @varlock/native-helper-win32-x64 1.19.0 → 1.20.0
  • varlock 1.19.0 → 1.20.0

patch Patch releases

  • env-spec-language 0.4.1 → 0.4.2

Bump files in this PR

Click here if you want to add another bump file to this PR


This comment is maintained by bumpy.

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size

⚠️ grows the bundle by 9.1 KB (+0.2%)

Metric main This PR Δ
Total dist 4557.6 KB 4566.7 KB +9.1 KB (+0.2%)
JS 1726.0 KB 1729.6 KB +3.5 KB (+0.2%)
Sourcemaps 2719.9 KB 2725.4 KB +5.6 KB (+0.2%)
Type defs 111.7 KB 111.7 KB
Other 0.0 KB 0.0 KB

dist/ only; native binaries are versioned separately and not counted here.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 14, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
varlock-website 3a8c2d7 Commit Preview URL

Branch Preview URL
Sep 15 2026, 09:37 AM

@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.

Important

The new token can silently absorb previously separate arguments or decorators when a later value supplies the closing slash.

Reviewed changes in 4068d47: the new regex tokenization path, escape preservation, runtime and editor flag parity, tests, documentation, and package bumps.

  • Atomic parser token: adds /pattern/flags handling to decorator values, function arguments, and object/array literal elements.
  • Escape preservation: marks regex literals so escaped dollar signs reach runtime regex construction unchanged.
  • Runtime and editor parity: recognizes the d and v JavaScript regex flags in Varlock and VS Code diagnostics.
  • Regression coverage and docs: exercises quantifier commas and other regex syntax across parser/runtime paths and documents the accepted literal form.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Comment thread packages/env-spec-parser/grammar.peggy Outdated
@pkg-pr-new

pkg-pr-new Bot commented Sep 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

@env-spec/parser

npm i https://pkg.pr.new/@env-spec/parser@1087

varlock

npm i https://pkg.pr.new/varlock@1087

@varlock/native-helper-darwin

npm i https://pkg.pr.new/@varlock/native-helper-darwin@1087

@varlock/native-helper-linux-arm64

npm i https://pkg.pr.new/@varlock/native-helper-linux-arm64@1087

@varlock/native-helper-linux-x64

npm i https://pkg.pr.new/@varlock/native-helper-linux-x64@1087

@varlock/native-helper-win32-x64

npm i https://pkg.pr.new/@varlock/native-helper-win32-x64@1087

commit: 3a8c2d7

@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.

Important

The new diagnostic introduces no additional issue, but the prior parser-boundary finding remains unresolved.

Reviewed changes since the prior Pullfrog review: the new commit added a targeted hint for regex literals split by an unescaped inner slash.

  • Split-regex hint: Detected malformed matches values that reach the existing mixed positional and named argument error, then attached guidance to escape the inner slash or quote the pattern.
  • Heuristic boundaries: Limited the hint to regex-accepting option names and kept similar path-shaped and unrelated mixed-argument errors unchanged.
  • Regression coverage: Added focused tests for the malformed URL regex, an unrelated mixed-argument call, and a similar path-shaped call.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

@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.

Important

The decorator boundary remains bypassable when separator whitespace is consumed as a regex escape.

Reviewed changes since the prior Pullfrog review: the decorator-specific regex token and the documentation of intentionally merged function arguments.

  • Protected ordinary decorator boundaries: Added decRegexLiteral so a regex candidate stops before an ordinary whitespace-@ sequence, including inside decorator function arguments.
  • Documented argument ambiguity: Recorded that slash-prefixed function arguments can merge through a later closing slash and explained that quoting preserves separate values.
  • Added regression coverage: Tested adjacent decorators, decorator function arguments, email-style patterns, and the intentional argument merge.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Comment thread packages/env-spec-parser/grammar.peggy Outdated

@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.

Important

The new regression tests introduce no additional issue, but the prior escaped-whitespace decorator-boundary finding remains unresolved.

Reviewed changes since the prior Pullfrog review: the new commit pinned the historical path truncation cases that caused the earlier regex grammar rule to be removed.

  • Covered item paths: Added exact assertions that slash-prefixed item values remain intact, with and without a trailing slash.
  • Covered function arguments: Added exact assertions that path-shaped function arguments are not partially consumed as regex literals.
  • Covered decorator values: Added an exact assertion that a slash-prefixed decorator value remains intact.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

`@type=string(matches=/^[0-9a-f]{7,40}$/)` fails with "cannot mix positional
args and named options": an unquoted `/pattern/` is an ordinary value, so the
comma in the quantifier ends it and `40}$/` becomes a positional arg.

The pattern is left alone - `/` stays an ordinary character, so paths are never
at risk of being read as regexes. Instead the explicit spelling is made complete
and the error says which one to reach for:

- regex("pattern", "flags") now takes flags as a second arg, so a pattern that
  cannot be written unquoted no longer loses the ability to carry them
- the mixed-args error carries a tip when the args look like a split pattern
- `d` and `v` flags are recognized alongside the rest
- docs state the actual rule, and stop calling regex() deprecated
@theoephraim
theoephraim force-pushed the parse-regex-literals-atomically branch from d2cfa5c to 70933e4 Compare September 15, 2026 05:50
@theoephraim theoephraim changed the title fix(env-spec-parser): parse regex literals as a single token fix(varlock): regex() takes flags, and say so when a pattern splits Sep 15, 2026

@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.

Important

The new two-argument regex() contract is not yet supported by VS Code diagnostics, which can mark runtime-valid values as invalid.

Reviewed changes since the prior Pullfrog review: the force-pushed replacement of grammar-level regex tokenization with an explicit regex(pattern, flags) path and targeted guidance for comma-split patterns.

  • Removed grammar tokenization: Reverted the atomic regex token and retained the established ordinary-value parsing rules, resolving the prior path and decorator boundary regressions.
  • Extended regex(): Added an optional static flags argument across runtime resolver and type-option conversion paths, with invalid-regex error handling.
  • Added split-pattern guidance: Attached a focused tip to existing mixed-argument errors when a matches pattern appears to have been split by a comma.
  • Updated flags and documentation: Added d and v slash-literal flags and documented when patterns must use the explicit wrapper.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

summary: '*(deprecated)* Creates a regular expression for use inside other functions.',
documentation: 'Deprecated — use `/pattern/flags` syntax instead. For example: `remap($VAR, /^dev.*/, result)`.',
summary: 'Creates a regular expression for use inside other functions.',
documentation: 'Takes a quoted pattern, with optional flags as a second argument. Prefer `/pattern/flags` when it fits (`remap($VAR, /^dev.*/, result)`), and use this when the pattern contains a `,`, a space, or a `)` - for example a quantifier: `regex("^[0-9a-f]{7,40}$")`.',

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.

This advertises regex("pattern", "flags"), but extractRegexPattern() still recognizes only the one-argument wrapper. String, URL, and domain matches diagnostics therefore compile the entire wrapper text as the pattern and can reject values that Varlock accepts.

Technical details
# VS Code diagnostics do not understand flagged regex wrappers

## Affected sites
- `packages/vscode-plugin/src/intellisense-catalog.ts:474` - advertises the new optional flags argument.
- `packages/vscode-plugin/src/diagnostics-core.ts:22` - matches only `regex("pattern")` and falls back to treating a two-argument wrapper as raw pattern text.
- `packages/vscode-plugin/src/diagnostics-core.ts:343` - string diagnostics use the incorrect extracted pattern.
- `packages/vscode-plugin/src/diagnostics-core.ts:457` - URL diagnostics use the same helper.
- `packages/vscode-plugin/src/diagnostics-core.ts:508` - domain diagnostics use the same helper.

## Required outcome
- Parse the optional quoted flags argument in `extractRegexPattern()` so editor validation agrees with runtime validation.
- Add diagnostic coverage for a value accepted only because of the second argument, such as a lowercase value matched by an uppercase pattern with `"i"`.

@theoephraim

Copy link
Copy Markdown
Member Author

Superseded approach: the first version of this PR lexed /pattern/flags as a grammar token. That is gone — the grammar is untouched now, and / stays an ordinary character, so pullfrog's finding about a token absorbing sibling values no longer applies to any code here.

The fix instead completes the explicit spelling (regex() gains flags) and points the existing error at it. Reasoning is in the description.

Filed #1089 for a separate problem found while probing this: quoting does not make a regex-shaped value literal, so the documented escape hatch in remap() does not work, and regex("/^abc$/") silently compiles a pattern that matches slashes. That fix is breaking and is not part of this PR.

regex() takes the pattern source, so a slash-wrapped argument compiled to a
pattern matching the slashes themselves - silently, and it is the obvious thing
to write when moving a pattern off the unquoted form the new tip points away
from. Flags have their own argument here, so there is no reason to carry them
inside the string either.

Rejected rather than stripped: regex("/usr/lib/") could legitimately be a
pattern for a path, so the error names both fixes instead of guessing. The
quoted-string case is the same bug and needs a breaking change - see #1089.
@theoephraim
theoephraim force-pushed the parse-regex-literals-atomically branch from 99bb50b to baa4a0b Compare September 15, 2026 08:18

@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.

Important

The new rejection is internally consistent, but it introduces a breaking configuration change under a minor Varlock release.

Reviewed changes since the prior Pullfrog review: the new commit rejected slash-wrapped pattern sources passed to regex() and documented the replacement syntax.

  • Rejected wrapped sources: Added the same focused SchemaError to the general resolver and @type option conversion paths.
  • Documented migration: Explained that callers must remove delimiter slashes or escape them when the slashes are part of the pattern.
  • Added regression coverage: Covered rejection of likely pasted literals and acceptance of escaped delimiter slashes.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

if (typeof regexStr !== 'string') {
throw new SchemaError('expects a string');
}
assertUnwrappedRegexSource(regexStr);

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.

This rejects previously valid configurations such as regex("/usr/lib/"), which the comment itself identifies as a legitimate pattern source. Since varlock is already 1.19.0, releasing this under the current minor changeset can break existing schemas during a routine upgrade.

Technical details
# Breaking regex source rejection is marked as minor

## Affected sites
- `packages/varlock/src/env-graph/lib/resolver.ts:517` - changes an accepted `regex()` source into a schema error.
- `.bumpy/regex-fn-flags.md:3` - requests only a minor bump for `varlock`.

## Required outcome
- Either preserve existing valid slash-matching sources, or publish the intentional rejection as a major Varlock change with migration guidance.

Quoting a regex literal whole - slashes and flags included - already works and
always has: matches="/^[0-9a-f]{7,40}$/i" carries the comma past the value rules
without changing what the value means. That is a one-character fix for the
reported error, so the tip and the docs name it first.

regex() stays the other spelling, for a pattern with enough slashes in it that
escaping each one hurts. The docs also drop two wrong claims: that a quoted
value is not detected as a regex, and that quoting a path-shaped match value in
remap() makes it literal. Neither is true - see #1089.

@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.

Important

The latest commit introduces no additional issue, but the two existing review findings remain unresolved.

Reviewed changes since the prior Pullfrog review: corrected the recommended syntax for transporting regex literals that contain parser delimiters.

  • Revised quoted-literal guidance: Documented that quoting the complete /pattern/flags value preserves regex detection while allowing commas, spaces, and closing parentheses.
  • Updated split-pattern tip: Pointed the existing mixed-argument diagnostic at the shorter quoted-literal fix while retaining regex("pattern", "flags") as an alternative.
  • Added behavioral coverage: Verified comma-containing quoted literals with and without flags against both matching and non-matching values.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Escaping the inner slashes and anchoring does it - `"/^\\/usr\\/lib\\/$/"` matches
`/usr/lib/` and nothing else. The previous wording said there was no way to do
this, which was wrong: it generalized from the bare escaped form (no wrapping
slashes), which opts out of regex detection and then compares against the raw
escaped string, so it never matches.

@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.

Important

The latest documentation correction introduces no new issue, but the two existing review findings remain unresolved.

Reviewed changes since the prior Pullfrog review: corrected and verified the documented way to match a slash-wrapped value exactly.

  • Corrected path guidance: Replaced the incorrect claim that slash-wrapped values cannot be matched literally with an escaped, anchored regex-literal example for remap().
  • Added behavioral coverage: Verified that the documented escaped pattern accepts /usr/lib/ and rejects a value that merely contains that path.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

- inner slashes in an unquoted literal are not required to be escaped: detection
  runs to the last slash, and the url tests already rely on that
- the tip's example showed a flag the user's pattern did not have
- pin the recommended quoted-literal spelling at the parser level too
- drop a stale 'deprecated' from a test name; the docs no longer call regex() that

@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.

Important

The latest commit introduces no new issue, but the two existing review findings remain unresolved.

Reviewed changes since the prior Pullfrog review: the documentation accuracy fixes, diagnostic wording refinement, parser regression coverage, and removal of stale deprecation language.

  • Clarified slash handling: Documented that inner forward slashes may remain unescaped because regex-literal detection uses the final slash as the delimiter.
  • Refined split-pattern guidance: Changed the diagnostic example so optional flags are clearly kept inside the quoted literal.
  • Expanded parser coverage: Added an exact parser assertion for a quoted comma-containing literal with flags.
  • Removed stale deprecation wording: Renamed the remaining regex() option test and documentation now that the wrapper is supported rather than deprecated.

Pullfrog  | Fix it ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant