# pattern - domain knowledge as data Source: https://truecopy.dev/docs/pattern/ Module: truecopy/pattern Everything that varies by market, by issuer or by document family should be a value, not a branch. And a pattern off the wire is untrusted input applied to untrusted input. Everything that varies by market, by issuer or by document family is a **value**, not a branch. A value can be served by a backend, refined by whoever reads the documents, versioned, and swapped for another market without a deploy. A branch can only be changed by whoever can change the code, which in practice means a release, a review and a week. ```ts import { compilePatterns, toRawPatterns, NEVER_MATCHES } from 'truecopy/pattern'; // Off the wire, as JSON const raw = [{ source: 'solde\\s{0,3}(cr[ée]diteur|d[ée]biteur)', flags: 'i' }]; const patterns = compilePatterns(raw); ``` Regular expressions do not survive JSON, so they travel as `{ source, flags }` and are rebuilt here. ## Which is exactly where the danger is > **A profile off the wire is untrusted input applied to untrusted input.** A pattern arrives from a backend and is then run, line by line, over a document somebody uploaded. Both ends are outside your control, and a catastrophically backtracking expression there is not slow: it is a tab that never comes back. So `compilePattern` guards before it compiles: - a source longer than a thousand characters is **rejected**; - a nested quantifier (the shape that makes backtracking explode) is **rejected**. And a rejected pattern compiles to `NEVER_MATCHES`, not to a throw. **The feature degrades and the page stays alive.** Throwing would take the whole reading down because one entry in a served profile was wrong. ## Counting occurrences ```ts import { countMatches } from 'truecopy/pattern'; countMatches(text, pattern); // how many times, bounded ``` Used by [classify](https://truecopy.dev/docs/classify/) for the rule that one occurrence may be a quotation and two rarely are. ## What this is not It is not a place to put your vocabulary. The library never reads your patterns' meaning: it compiles them, guards them and counts them. Which words belong to a statement and which to an invoice is yours, and it belongs in a profile you can change without asking anyone. Map of this site for a model: https://truecopy.dev/llms.txt Every page in one file: https://truecopy.dev/llms-full.txt