9.1 KiB
Architecture
Regex Tools is a static browser application with framework-neutral regex boundaries.
React workbench
├─ Pattern SyntaxSupervisor → syntax.worker
│ ├─ Regexpp ECMAScript provider → normalized AST
│ ├─ PCRE2 lexical provider → partial normalized structure
│ ├─ Python lexical provider → partial normalized structure
│ └─ Java lexical provider → partial normalized structure
├─ Replacement SyntaxSupervisor → syntax.worker → typed replacement tokens
├─ EngineSupervisor
│ ├─ ecmascript.worker → native RegExp → match DTOs
│ ├─ pcre2.worker → PCRE2 10.47 WASM ABI → match DTOs
│ ├─ python.worker → Pyodide 314.0.3 → CPython 3.14.2 re → match DTOs
│ └─ java.worker → TeaVM 0.15.0 java.util.regex module → match DTOs
├─ Pcre2TraceSupervisor → pcre2-trace.worker
│ └─ separate ABI-v3 PCRE2_AUTO_CALLOUT operation → bounded trace DTOs
├─ AnalysisPanel
│ ├─ normalized-AST ECMAScript static heuristics
│ └─ AnalysisSupervisor → analysis.worker → native RegExp benchmark/growth probes
├─ ComparisonOrchestrator
│ ├─ two exact syntax snapshots + independently killable engine workers
│ └─ normalized UTF-16 match/capture alignment + explicit incomplete states
├─ PCRE2 C17 generator → validated snapshot → exact UTF-8 byte-array source
├─ SubjectMinimizer
│ ├─ fixed syntax snapshots + exact real-engine failure or comparison oracle
│ └─ deterministic Unicode-scalar reducer → bounded local-minimal result
├─ CaseGenerationOrchestrator
│ ├─ generation.worker → deterministic bounded ECMAScript AST candidates
│ └─ EngineSupervisor → selected actual-engine label verification
├─ ECMAScript literal/control formatter
│ ├─ exact regexpp literal-token ranges → idempotent candidate preview
│ └─ PatternFormatValidator → paired syntax + actual-engine workers and exact tests
├─ Corpus EngineSupervisor → sequential bounded document jobs → summaries/outputs
├─ Test SyntaxSupervisor + EngineSupervisor → isolated cancellable test runs
└─ project validator / serializer / IndexedDB persistence
Provider AST objects never cross the worker boundary. The syntax worker emits application-owned nodes, tokens, diagnostics, capture definitions and explicit provenance. React never imports or traverses regexpp's cyclic AST.
Every engine request carries a protocol version, monotonically increasing request ID and worker generation. A supervisor allows one active request, rejects stale responses, terminates on timeout/crash/cancel, and lazily creates a new generation. Timeout is a distinct error state. A separate idempotent load operation verifies a worker's runtime identity before execution; its flavour-specific startup deadline is not charged to the 250 ms live or selected manual execution deadline. A killed generation must load and verify again.
The Python worker imports only the self-hosted Pyodide loader and pack, then
evaluates the application-owned bounded bridge in CPython. The bridge returns
code-point ranges, which are retained as native values and converted only at
valid boundaries to editor UTF-16. The Java worker imports a verified TeaVM
ES2015 module. That module executes TeaVM's own java.util.regex.Pattern and
Matcher class-library implementation and returns native UTF-16 ranges; it is
not OpenJDK.
PCRE2 tracing is not an EngineSupervisor match or replacement operation. Its
own worker compiles with PCRE2_AUTO_CALLOUT, copies only complete fixed
records and bounded mark bytes, and stops natively at either trace cap.
Reported callout fields retain byte positions; movement classifications are a
separate derived UI layer. Normal execution, corpus, tests and benchmarks never
run through the trace operation.
Static analysis traverses the normalized ECMAScript tree on the UI side under node/finding caps. Timing and generated-input growth probes use the independent analysis worker. Timeout, cancellation or crash discards that worker; no trace mode is benchmarked.
Comparison is a separate orchestration layer, not an EngineSupervisor
operation. It runs explicit ECMAScript and PCRE2 syntax/execution snapshots,
retains each engine identity and native range model, and aligns only normalized
editor ranges. Timeout, cancellation, compile rejection and truncation remain
distinct non-comparable outcomes. Shared patterns are sent unchanged;
per-flavour ports are user-owned variants.
The PCRE2 C generator is application-owned and never implements browser execution. It validates one exact PCRE2 snapshot, emits user strings as independent UTF-8 byte arrays, and states the host-level mappings that the PCRE2 API cannot express directly. Its advertised C17 target is protected by deterministic golden identities and an exact PCRE2 10.47 compile/execute gate.
Subject minimization is a main-thread bounded reducer whose expensive predicate checks stay in independently terminable ECMAScript or PCRE2 workers. Fixed pattern syntax is parsed once for capture metadata. Python and Java are blocked at this feature boundary rather than routed to a different engine. The baseline and every accepted candidate retain the exact target and engine identity. Timeout, crash, cancellation, worker failure and incomplete output are separate outcomes. Deterministic ddmin deletion is followed by a fixed-point local sweep over Unicode-scalar deletion and lower-rank canonical replacement; only completion of that sweep permits a transform-local minimality claim.
Generated-case synthesis and verification are separate trust boundaries. The
dedicated generation worker receives only a validated, complete ECMAScript
normalized tree plus explicit settings and seed. The main-thread orchestrator
then sends each bounded candidate to EngineSupervisor; only the selected real
adapter can establish a should-match or should-not-match label. Worker
timeout, crash, cancellation, compile rejection, wrong outcome and ordinary
failure stay distinct. Settings/results are ephemeral, while explicitly added
unit tests retain field-validated generator provenance.
Formatting is not an engine operation and never rewrites from heuristic text alone. The ECMAScript formatter consumes an exact accepted regexpp snapshot and changes only parser-reported literal/control token ranges. Its validator reparses source and candidate separately, preserves capture shape, runs both in separate actual-engine workers against the current replacement snapshot, then replays every enabled test tied to the exact active pattern/version/flags/options. Any difference, incomplete output, one-sided timeout, identity change or inconclusive worker outcome blocks apply. The preview and observations are ephemeral, and user confirmation is required even after all bounded gates pass.
Pattern and replacement syntax use independent supervisors. A pattern snapshot is bound to its exact pattern, ordered flags and revision; execution cannot use acceptance or capture metadata from an earlier input. Replacement output is built while the native engine produces the same bounded authoritative match sequence that is returned to the UI.
Per-match replacement ranges and token contributions are deterministic mappings over those actual matches, the complete replacement-token model and the bounded output. They execute no replacement callback or user code and have separate presentation and token-evaluation limits.
Corpus documents are ephemeral React session state, not project state. A dedicated engine supervisor processes one document at a time and is terminated on cancellation, timeout or workspace exit. Batch orchestration retains only per-document summaries and, for apply runs, bounded outputs. It enforces document-count, per-document byte, aggregate input byte, aggregate match, aggregate output and batch wall-time limits. Exact-output export is unavailable if any document is partial, failed, cancelled or not run.
Whole-document mode sends each document to the engine once. Independent-line mode segments CRLF, CR, LF, U+2028 and U+2029 without discarding them, executes each logical line as its own subject, and reattaches the exact original separator during apply. This makes anchor and cross-line behavior deliberately different rather than silently rewriting the pattern. Match DTOs are reduced to bounded per-group participation counts and samples before the next document.
Native ranges are retained in the engine's declared unit. ECMAScript and Java
use UTF-16 code units. PCRE2 always uses UTF/UCP and exposes UTF-8 byte ranges;
its ABI copies scalar records and names out of WebAssembly before returning and
retains no code pointer. CPython re exposes Unicode code-point indices.
Browser editor ranges are half-open UTF-16 code-unit ranges. Reusable
converters cover UTF-8 byte and Unicode code-point offsets, including
invalid-boundary rejection and lone-surrogate detection.
The release boundary is dist/ plus checked-in legal/source documents. The
Portal consumes the resulting immutable ZIP and never imports React source.