/** * Peppol Identifier validation rules. * * Peppol identifiers address businesses on the Peppol network. * Each identifier is a (scheme, identifier) tuple where the scheme * determines the identifier's format (e.g. "GB:VAT" for a UK VAT number, * "0002" for French SIRENE). * * This module provides: * - SCHEMES_BY_COUNTRY: guided options, keyed by ISO country code * ⚠️ Do not restate the COUNT here — it said "5 main markets" while the map * held six (GPR-1046). Read the object. * ⚠️ Nor the LIST, which is the same defect wearing different clothes: it read * "(UK/FR/DE/IE/BE/NL)" while the map already held Denmark (GPR-1198), and * Sweden made it wrong a second time (GPR-1199). An enumeration nobody can * test goes stale exactly when the map grows. * - validatePeppolIdentifier: format check with fallback support for any scheme */ export interface SchemeOption { /** Peppol scheme code (e.g. "GB:VAT", "0002"). */ code: string; /** Human-readable label shown in the UI. */ label: string; /** One-line description (what the identifier represents). */ description: string; /** Example value to show in placeholder / hint. */ example: string; /** * Alternative spellings that denote this SAME scheme, matched case-insensitively. * * Provider vocabularies diverge from the numeric EAS codes: Storecove names the * German Leitweg-ID `DE:LWID` where Peppol calls it `0204`. Without this, the * format rule attaches to one spelling and the other is silently unvalidated — * `de:lwid` + `"not-a-leitweg-id"` used to pass (GPR-966, V.E.R.O.N.I.C.A. finding). */ aliases?: string[]; /** Whether this scheme is the recommended default for the country. */ recommended?: boolean; /** * Optional regex to validate the identifier format WHEN ADDRESSING someone. * * Calibrated on participants that really exist on the network — including forms * the network carries but no provider will let you register. See `registration` * below for why the two cannot be the same rule. */ pattern?: RegExp; /** Short text describing the expected format. */ formatHint: string; /** * STRICTER rule applied only when REGISTERING this identifier as your own * (GPR-1059). Absent = registration accepts exactly what `pattern` accepts. * * ## Why a second rule exists at all * * `pattern` answers "can I address this participant?" and is measured against * the live Peppol Directory. Registration answers "will my access point accept * this as my identity?" and is measured against the provider. They diverge, and * OpenPeppol arbitrates neither: the published code list * (`participant-identifier-schemes-v9.7.json`, versioned in the console) carries * NO `structure` and NO `validation-rules` for `9930`, `9932` or `9935` — only * `state` and `registrable`, where `0184` and `0208` do carry them. * * ⛔ So do NOT "reconcile" the two by tightening `pattern`. 39 of the 390 live * `9932:` participants carry no country prefix, as do 37 of the 282 `9935:` * ones; `ie9z42326ga` is a real participant whose exact form the provider * refuses to register. Tightening addressing would make real recipients * unaddressable to stop a mistake that belongs to a different question. * * ⚠️ These are DATED infrastructure facts, not a spec. If a legitimate * registration starts failing, re-measure against the provider — never widen a * rule to make a test pass. Corpus and method live in the test file beside this * one; the same measurement backs the public docs table * (`packages/website/src/__tests__/peppol-scheme-formats.test.mjs`). */ registration?: { pattern: RegExp; /** Names what registration requires — never a copy of `formatHint`. */ formatHint: string; }; } export declare const SCHEMES_BY_COUNTRY: Record; export interface IdentifierValidationResult { valid: boolean; error?: string; /** True if a checksum was applied. Not all of them are country-specific: * `0088` (GLN) and `0199` (LEI) are international schemes. */ checksumChecked?: boolean; } /** * What the caller is about to do with this identifier (GPR-1059). * * - `addressing` — check a RECIPIENT before sending to them. Permissive on * purpose: it must accept every form the Peppol network actually carries. * - `registration` — check YOUR OWN identifier before claiming it. Stricter for * the schemes where the access point imposes a narrower format than the * network carries. * * Defaults to `addressing`, which leaves the per-scheme PATTERN rules unchanged. * * ⚠️ GPR-1350 — `addressing` is no longer unconditionally permissive, and this * line used to say it kept "the published contract unchanged", which is now false * and ships in the published `.d.ts`. POLICY 1's alphabet is checked under BOTH * usages, so a value carrying a character the Peppol network cannot carry is * refused for addressing too. That is the point: `addressing` promises to accept * every form the network ACTUALLY carries, and the network carries none of these. */ export type IdentifierUsage = "addressing" | "registration"; export interface ValidateIdentifierOptions { /** Defaults to `"addressing"`. */ usage?: IdentifierUsage; } /** * Validate a (scheme, identifier) tuple before submission. * * - Scheme must match Peppol conventions: 4 digits OR country prefix "XX:YYY". * - Identifier must be non-empty, <= 64 chars, with no leading/trailing whitespace. * - **Identifier may only contain `a-z`, `A-Z`, `0-9`, `-`, `.`, `_` or `~`** * (Peppol Policy for use of Identifiers 4.4.0, POLICY 1 — GPR-1350). Applied * under BOTH usages, and BEFORE the per-scheme pattern, because most routable * schemes carry no pattern at all and this is the only check they ever meet. * ⚠️ NARROWER than "ASCII": a space, a slash and a colon are refused. * ⚠️ **This is a behaviour change for callers**: a value outside the set that a * pattern-less scheme used to accept — e.g. `("GB:CRN", "AB/CD")` — now returns * `valid: false`. See the CHANGELOG entry for GPR-1350. * - If the scheme is listed in SCHEMES_BY_COUNTRY with a pattern, the * identifier must match that pattern. * - Under `usage: "registration"`, a scheme carrying a `registration` rule is * checked against THAT pattern instead — see `SchemeOption.registration` for * why the two questions cannot share one rule. * - If the scheme has a checksum (FR/DE/BE, plus the international GLN and * LEI), it is applied under BOTH usages. `checksumChecked` reports whether * this ran. Read `CHECKSUM_BY_SCHEME` for the current set rather than this * list — a prose enumeration next to a real one always ends up the stale one. * * ⛔ The stricter rule REPLACES the addressing pattern rather than adding to it, * and that is safe only because every `registration` pattern shipped here accepts * a strict subset of its `pattern`. A future rule that accepted something * addressing refuses would make the registration door the permissive one — the * test file asserts the subset property on the measured corpus. */ export declare function validatePeppolIdentifier(scheme: string, identifier: string, options?: ValidateIdentifierOptions): IdentifierValidationResult; //# sourceMappingURL=peppol-identifier-rules.d.ts.map