Skip to content

feat(varlock)!: stop reading strings as regexes - #1091

Open
theoephraim wants to merge 1 commit into
mainfrom
remove-regex-string-interpretation
Open

theoephraim wants to merge 1 commit into
mainfrom
remove-regex-string-interpretation

Conversation

@theoephraim

Copy link
Copy Markdown
Member

Closes #1089. The breaking half of #1087.

Caution

Do not merge until the release containing #1087 has shipped. That release carries the deprecation warning. Merged before it, the warning and the removal land in the same version and nobody gets the migration window. .bumpy/regex-fn-flags.md is still in the tree, so that release has not happened yet.

What changes

A pattern must be a regex("pattern", "flags") call. A string is no longer read as a regex anywhere:

  • matches (string, url, domain, and nested element/key types): any string -- /^abc$/i, "/^abc$/i", or a plain "^abc$" -- is a schema error whose tip shows the exact regex() call to write. A pattern referenced from another variable (matches=$PATTERN) is an error at plan time: a reference can only be a string, and an empty optional item never resolves its type parts, so waiting for a value would miss it. Any other resolver-valued option that resolves to a string errors at the final build. One that resolves to a regex() (if(cond, regex(a), regex(b))) is unaffected.
  • remap() match values are compared exactly, like any other string. remap($DIR, /usr/lib/, x) finally means what it looks like. regex() match values test as before.
  • @auditExtraPatterns() takes regex() calls only; the quoted-string carve-out is gone.
  • VSCode diagnostics extract a pattern from a regex() call only.
  • parseRegexLikeString and everything that fed the deprecation warning are deleted.

regex() itself is unchanged: literal string argument, optional flags, and it still rejects a /.../-wrapped argument. That guard is most useful right at this boundary -- someone migrating off /^abc$/ is exactly the person who writes regex("/^abc$/") -- and can be relaxed in a later minor once the habit has died out.

Plugin-registered types with their own matches setting are untouched; the rule is scoped to the built-in types that read matches as a pattern.

Why

Inference from shape, at runtime, after quotes were stripped, was the source of every confusion in #1087: quoting never made a value literal, /usr/lib/ was a regex in one position and a path in another, two spellings had different slash rules, and a comma quietly broke one of them. The mechanism is removed rather than documented.

Dynamic patterns (a regex built from another variable) are deliberately not supported: a validation rule supplied by the environment being validated inverts what the rule is for. A dynamic choice between static regex() calls still works.

Migration

/^abc$/i becomes regex("^abc$", "i"); "^abc$" becomes regex("^abc$"). Every error carries the exact replacement, and the warning shipped in #1087 says the same thing at every site that changes.

The breaking half of #1087. A pattern must be a regex("pattern", "flags")
call; a string is no longer read as a regex anywhere.

- matches (string, url, domain, nested types): any string is a schema error
  whose tip shows the exact regex() call. A referenced pattern
  (matches=$PATTERN) errors at plan time - a reference can only be a string,
  and an empty optional item never resolves its type parts. Any other
  resolver-valued option that resolves to a string errors at the final build.
- remap() match values are compared exactly, like any other string.
- @auditExtraPatterns() takes regex() calls only.
- VSCode diagnostics extract a pattern from a regex() call only.
- parseRegexLikeString and the deprecation machinery are deleted.

Plugin types with their own matches setting are untouched. regex() itself is
unchanged, including the guard against a /.../-wrapped argument, which is most
useful right at this boundary.
@theoephraim theoephraim added the BREAKING This is a breaking change in varlock core - merge when doing a new major release label Sep 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

bumpy-frog

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

major Major releases

  • @varlock/1password-plugin 2.0.4 → 3.0.0 (dep)
  • @varlock/akeyless-plugin 2.0.1 → 3.0.0 (dep)
  • @varlock/astro-integration 1.4.2 → 2.0.0 (dep)
  • @varlock/aws-secrets-plugin 1.2.3 → 2.0.0 (dep)
  • @varlock/aws-sigv4-plugin 0.1.0 → 1.0.0 (dep)
  • @varlock/azure-key-vault-plugin 1.2.3 → 2.0.0 (dep)
  • @varlock/bitwarden-plugin 2.0.1 → 3.0.0 (dep)
  • @varlock/cloudflare-integration 1.5.2 → 2.0.0 (dep)
  • @varlock/dashlane-plugin 2.1.1 → 3.0.0 (dep)
  • @varlock/doppler-plugin 2.0.1 → 3.0.0 (dep)
  • @varlock/expo-integration 1.2.1 → 2.0.0 (dep)
  • @varlock/google-secret-manager-plugin 1.2.2 → 2.0.0 (dep)
  • @varlock/hashicorp-vault-plugin 2.1.1 → 3.0.0 (dep)
  • @varlock/infisical-plugin 2.1.1 → 3.0.0 (dep)
  • @varlock/keepass-plugin 2.0.2 → 3.0.0 (dep)
  • @varlock/keeper-plugin 2.0.1 → 3.0.0 (dep)
  • @varlock/kubernetes-plugin 1.0.1 → 2.0.0 (dep)
  • @varlock/native-helper-darwin 1.19.0 → 2.0.0
  • @varlock/native-helper-linux-arm64 1.19.0 → 2.0.0
  • @varlock/native-helper-linux-x64 1.19.0 → 2.0.0
  • @varlock/native-helper-win32-x64 1.19.0 → 2.0.0
  • @varlock/nextjs-integration 1.2.2 → 2.0.0 (dep)
  • @varlock/nuxt-integration 0.1.2 → 1.0.0 (dep)
  • @varlock/pass-plugin 1.0.2 → 2.0.0 (dep)
  • @varlock/passbolt-plugin 2.0.1 → 3.0.0 (dep)
  • @varlock/proton-pass-plugin 2.0.1 → 3.0.0 (dep)
  • @varlock/vite-integration 1.5.2 → 2.0.0 (dep)
  • varlock 1.19.0 → 2.0.0

minor Minor releases

  • env-spec-language 0.4.1 → 0.5.0

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

Copy link
Copy Markdown
Contributor

📦 Bundle size

✅ shrinks the bundle by 11.1 KB (−0.2%)

Metric main This PR Δ
Total dist 4579.8 KB 4568.7 KB −11.1 KB (−0.2%)
JS 1734.0 KB 1730.2 KB −3.8 KB (−0.2%)
Sourcemaps 2733.6 KB 2726.7 KB −6.9 KB (−0.3%)
Type defs 112.2 KB 111.7 KB −0.5 KB (−0.4%)
Other 0.0 KB 0.0 KB

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

@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

Deferred validation, migration guidance, and the VS Code test suite need fixes before this can merge.

Reviewed changes in c090f982, covering the breaking removal of implicit string-to-regex behavior across the env graph, CLI audit patterns, editor diagnostics, tests, docs, and release metadata.

  • Built-in matches options: Static strings and direct references now produce schema errors, while explicit regex() calls and resolver-selected regexes remain supported.
  • remap() semantics: Slash-wrapped strings now compare exactly, while RegExp values continue to test the source value.
  • Audit patterns: @auditExtraPatterns() now accepts only regex() results and removes its deprecation warning path.
  • Editor and docs: VS Code pattern extraction and public documentation now describe regex() as the only pattern syntax.
  • Release metadata: Adds a major varlock bump and minor env-spec-language bump for the breaking behavior change.

⚠️ The deprecation release has not shipped yet

The PR correctly states that this removal must follow the release containing #1087, but .bumpy/regex-fn-flags.md is still present at this head. Merging before that release would publish the warning and removal together, eliminating the promised migration window.

Technical details
# Preserve the staged rollout

## Affected sites
- `.bumpy/regex-fn-flags.md` - the deprecation change is still pending release
- `.bumpy/regex-strings-removed.md` - this PR schedules the breaking removal

## Required outcome
- Ship the #1087 deprecation release before merging or releasing this removal.

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

if (readsOptionAsRegex(name, key) && typeof val === 'string') {
pushWarningOnce(ctx.warnings, deprecatedRegexStringWarning(val, `${context} - option "${key}"`));
if (settings[key] instanceof DeferredValue && readsOptionAsRegex(name, key) && typeof val === 'string') {
throw stringPatternError(val, `${context} - option "${key}"`);

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 check is reached only during the final type build, but ConfigItem returns for an empty optional value before resolving deferred type parts. Consequently, matches=if(true, "^[A-Z]+$", "x") leaves an empty item valid with no errors and makes the same schema invalid only when the item gains a value; reject unsupported string-producing patterns independently of item presence.

Technical details
# Deferred string patterns bypass validation on empty items

## Affected sites
- `packages/varlock/src/env-graph/lib/type-decorator.ts:509-510` - string results are rejected only during `build(resolved)`
- `packages/varlock/src/env-graph/lib/config-item.ts:1030-1045` - empty values return before deferred type parts are resolved

## Required outcome
- An unsupported resolver-valued string pattern must invalidate the schema consistently even when the item is currently empty.
- Preserve support for resolver expressions that resolve to explicit `regex()` values.

if (sink.some((w) => w.message === warning.message && w.tip === warning.tip)) return;
sink.push(warning);
/** quote a pattern for an env-spec example - only `"` needs escaping inside a double-quoted value */
const quoteForExample = (str: string) => `"${str.replaceAll('"', '\\"')}"`;

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.

Always emitting double quotes does not preserve single-quoted patterns containing $NAME, because env-spec expands references inside double quotes. For example, matches='[$FOO]' suggests regex("[$FOO]"); pasting that suggestion produces Invalid function args instead of the original static pattern, so the replacement must use parser-safe quoting that preserves literal text.

Technical details
# Migration tip changes literal pattern semantics

## Affected sites
- `packages/varlock/src/env-graph/lib/type-decorator.ts:56-68` - `quoteForExample` always selects double quotes
- `packages/env-spec-parser/src/expand.ts:95-101` and `144-150` - double-quoted `$NAME` text is expanded, while single-quoted text is not

## Required outcome
- Every emitted `regex()` suggestion must parse as a static pattern with the same source and flags as the rejected string.

const regexLiteral = value.match(/^\/(.*)\/([dgimsuvy]*)$/s);
if (regexLiteral) return { pattern: regexLiteral[1].replaceAll('\\/', '/'), flags: regexLiteral[2] };
return { pattern: value, flags: '' };
return undefined;

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 behavior change leaves four existing diagnostics tests failing because they still expect plain and slash-wrapped strings to be interpreted as patterns. Update those expectations for the new contract so bun run --filter env-spec-language test:ci passes again.

@cloudflare-workers-and-pages

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 c090f98 Commit Preview URL

Branch Preview URL
Sep 15 2026, 08:45 PM

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

Labels

BREAKING This is a breaking change in varlock core - merge when doing a new major release core:varlock core:vscode-plugin core:website maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop reading /pattern/ strings as regexes (breaking, next major)

1 participant