{"_id":"xml-naming","_rev":"3-3c1bd5ba3623bff46f6c549247a6ff39","name":"xml-naming","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"xml-naming","version":"0.1.0","keywords":["xml","xml-name","ncname","qname","nmtoken","xml-validation","xml-namespace","name-validator","xml-1.0","xml-1.1"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"xml-naming@0.1.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/xml-naming#readme","bugs":{"url":"https://github.com/NaturalIntelligence/xml-naming/issues"},"dist":{"shasum":"8ab7106c5b8d23caa2fabac1cadf17136379fbd8","tarball":"https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz","fileCount":4,"integrity":"sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==","signatures":[{"sig":"MEUCIGyytaXSKvH1ewBd80GW54AmXiPn9RSGDH1z9/cp1cMXAiEA8Ri3DWYQVvUw1hB57mDwiuqa7fGNzykuYlHcoQ7hPjY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":18717},"main":"./src/index.js","type":"module","types":"./src/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"scripts":{"test":"jasmine specs/*spec.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/xml-naming.git","type":"git"},"_npmVersion":"11.11.0","description":"Validates XML name productions — Name, NCName, QName, NMToken, NMTokens — for XML 1.0 and 1.1","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"jasmine":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/xml-naming_0.1.0_1778221254081_0.5101344707614521","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"xml-naming","version":"0.2.0","keywords":["fast","xml","xml-name","ncname","qname","nmtoken","xml-validation","xml-namespace","name-validator","xml-1.0","xml-1.1"],"author":{"url":"https://solothought.com","name":"Amit Gupta"},"license":"MIT","_id":"xml-naming@0.2.0","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"homepage":"https://github.com/NaturalIntelligence/xml-naming#readme","bugs":{"url":"https://github.com/NaturalIntelligence/xml-naming/issues"},"dist":{"shasum":"e417fa26235eeb8a4cb80b10909fced8e719fd8b","tarball":"https://registry.npmjs.org/xml-naming/-/xml-naming-0.2.0.tgz","fileCount":5,"integrity":"sha512-4mE8FXIZZfHHKYv1unWHT3BAgskSyTgJG/q4bRV4HswaNK75KDbOwndDgmnUpMexbgsKzYPk1fOekXmf7oZ1yA==","signatures":[{"sig":"MEUCIEovmkgAZuCNN6yWM26zQHWCA+N/mGfj9Xzq6jJDl33lAiEAkP/5a97n/ywZJwN3CFX1ASUTKtMdKOfZDDpYe2R+TdI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":24976},"main":"./src/index.js","type":"module","types":"./src/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./src/index.d.ts","import":"./src/index.js"}},"funding":[{"url":"https://github.com/sponsors/NaturalIntelligence","type":"github"}],"gitHead":"e89979e2dd66f9392454e9c3c5c8b072e3987f78","scripts":{"perf":"node specs/perf.js","test":"jasmine specs/*spec.js"},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"repository":{"url":"git+https://github.com/NaturalIntelligence/xml-naming.git","type":"git"},"_npmVersion":"11.11.0","description":"Validates XML name productions — Name, NCName, QName, NMToken, NMTokens — for XML 1.0 and 1.1","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"jasmine":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/xml-naming_0.2.0_1782888763517_0.3306834822009097","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"xml-naming","version":"0.3.0","description":"Validates XML name productions — Name, NCName, QName, NMToken, NMTokens — for XML 1.0 and 1.1","type":"module","main":"./src/index.js","types":"./src/index.d.ts","exports":{".":{"import":"./src/index.js","types":"./src/index.d.ts"}},"scripts":{"test":"jasmine specs/*spec.js","perf":"node specs/perf.js"},"keywords":["fast","xml","xml-name","ncname","qname","nmtoken","xml-validation","xml-namespace","name-validator","xml-1.0","xml-1.1"],"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","funding":[{"type":"github","url":"https://github.com/sponsors/NaturalIntelligence"}],"devDependencies":{"jasmine":"^5.0.0"},"engines":{"node":">=16.0.0"},"repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/xml-naming.git"},"bugs":{"url":"https://github.com/NaturalIntelligence/xml-naming/issues"},"gitHead":"f732ff38aa3e343ef64d190f0c96f370cb357969","_id":"xml-naming@0.3.0","homepage":"https://github.com/NaturalIntelligence/xml-naming#readme","_nodeVersion":"22.14.0","_npmVersion":"11.11.0","dist":{"integrity":"sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==","shasum":"46c1e18bfe2858479982dd2accf34d16e749eda2","tarball":"https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz","fileCount":5,"unpackedSize":31395,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDXlhFeK10cvNRgEhlV/6w+1RoxWXyWKNP2hsqVYwuXMAIhALM5Zh0HA/rWBjdthdgmiVvRKTt6NeCaPekLxs5Zapa4"}]},"_npmUser":{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"},"directories":{},"maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/xml-naming_0.3.0_1782979556291_0.6728613378693207"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-08T06:20:54.081Z","modified":"2026-07-02T08:05:56.539Z","0.1.0":"2026-05-08T06:20:54.228Z","0.2.0":"2026-07-01T06:52:43.653Z","0.3.0":"2026-07-02T08:05:56.436Z"},"bugs":{"url":"https://github.com/NaturalIntelligence/xml-naming/issues"},"author":{"name":"Amit Gupta","url":"https://solothought.com"},"license":"MIT","homepage":"https://github.com/NaturalIntelligence/xml-naming#readme","keywords":["fast","xml","xml-name","ncname","qname","nmtoken","xml-validation","xml-namespace","name-validator","xml-1.0","xml-1.1"],"repository":{"type":"git","url":"git+https://github.com/NaturalIntelligence/xml-naming.git"},"description":"Validates XML name productions — Name, NCName, QName, NMToken, NMTokens — for XML 1.0 and 1.1","maintainers":[{"name":"amitgupta","email":"amitgupta.gwl@gmail.com"}],"readme":"# xml-naming\n[![xml-naming downloads](https://img.shields.io/npm/dw/xml-naming.svg)](https://npm-compare.com/xml-naming) \n[![xml-naming version](https://img.shields.io/npm/v/xml-naming.svg)](https://www.npmjs.com/package/xml-naming)\n[![xml-naming license](https://img.shields.io/npm/l/xml-naming.svg)](https://github.com/NaturalIntelligence/xml-naming)\n\nValidates XML name productions as defined in the [XML 1.0](https://www.w3.org/TR/xml/) and [XML 1.1](https://www.w3.org/TR/xml11/) specifications.\n\nCovers all five productions:\n\n| Production | Description | Colon | Digit/hyphen start |\n|---|---|---|---|\n| `Name` | General XML name | ✅ | ❌ |\n| `NCName` | Non-Colonized name | ❌ | ❌ |\n| `QName` | Namespace-qualified name (`prefix:local`) | ✅ (one only) | ❌ |\n| `NMToken` | Name token (relaxed start) | ✅ | ✅ |\n| `NMTokens` | Whitespace-separated NMToken list | ✅ | ✅ |\n\nUsed internally by [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser), [fast-xml-validator](https://github.com/NaturalIntelligence/fast-xml-validator), [@nodable\\flexible-xml-parser](https://github.com/nodable/flexible-xml-parser)  and [fast-svg-parser](https://github.com/amitguptagwl/fast-svg-parser).\n\n---\n\n## Install\n\n```bash\nnpm install xml-naming\n```\n\n---\n\n## Usage\n\n### Boolean validators\n\n```js\nimport { name, ncName, qName, nmToken, nmTokens } from 'xml-naming';\n\n// Name — colon allowed anywhere, used for DOCTYPE entity names\nname('foo')          // true\nname('a:b:c')        // true  ← multiple colons fine for Name\nname('1foo')         // false ← digit start invalid\n\n// NCName — no colon, used for SVG id attributes, namespace prefixes\nncName('my-id')      // true\nncName('xlink:href') // false ← colon not allowed\n\n// QName — exactly one colon as prefix separator, used for element/attribute names\nqName('svg:circle')  // true\nqName('foo')         // true  ← unprefixed QName is valid\nqName('a:b:c')       // false ← only one colon allowed\nqName(':foo')        // false ← cannot start with colon\n\n// NMToken — any NameChar at start, used for DTD NMTOKEN attributes\nnmToken('123')       // true  ← digit start is fine\nnmToken('-bar')      // true\nnmToken('foo bar')   // false ← space not allowed\n\n// NMTokens — whitespace-separated NMToken list\nnmTokens('tok1 tok2 -foo 123')  // true\n```\n\n### XML version option\n\nAll validators accept an optional `{ xmlVersion }` option:\n\n```js\nimport { name } from 'xml-naming';\n\nname('\\u0085', { xmlVersion: '1.0' })  // false — NEL (Next Line), not in 1.0 ranges\nname('\\u0085', { xmlVersion: '1.1' })  // true  — explicitly allowed in 1.1\n\nname('\\uD800\\uDC00', { xmlVersion: '1.0' })  // false\nname('\\uD800\\uDC00', { xmlVersion: '1.1' })  // true\n```\n\n---\n\n### ASCII-only fast path\n\nAll validators, `validate`, `validateAll`, and `sanitize` also accept `{ asciiOnly: true }`.\nWhen set, matching is restricted to the ASCII subset of the NameStartChar/NameChar\nproductions and skips unicode-aware regex matching entirely — no `\\u00C0-\\uFFFD`-style\nranges, and (for XML 1.1) no `/u` regex flag. Unicode-aware regexes are measurably slower\nthan plain ASCII matching in JS engines, so this is a real performance win when you know\nyour input is ASCII-only, which is the common case for HTML/SVG ids and most XML tags.\n\n**This is opt-in and defaults to `false`** for backward compatibility: turning it on\nchanges behavior, since it rejects legitimate non-ASCII XML names that would otherwise be\nvalid. Only enable it when you control the input and know it's ASCII (e.g. internal\nidentifiers, machine-generated names), not for validating arbitrary user- or\nexternally-supplied XML/SVG content.\n\n```js\nimport { name, sanitize } from 'xml-naming';\n\nname('café', { asciiOnly: true })   // false — 'é' is not ASCII, even though it's\nname('café')                        //  true    a valid XML 1.0/1.1 NameChar\n\nsanitize('café', 'name', { asciiOnly: true })  // 'caf_' — non-ASCII replaced too\nsanitize('café', 'name')                       // 'café' — left untouched by default\n```\n\n---\n\n### Memoized validator (`createValidator`)\n\nReal documents tend to reuse a small vocabulary of tag/attribute names across many\nsiblings (`id`, `class`, `href`, ... repeated across hundreds of elements). Calling the\nplain boolean validators re-runs the regex on every call, even for names seen before.\n\n`createValidator(production, opts)` returns a memoized validator function: `xmlVersion`\nand `asciiOnly` are fixed at creation time (so the regex is resolved once, not per call),\nand repeated inputs after the first are served from an internal cache instead of\nre-matching the regex.\n\n```js\nimport { createValidator } from 'xml-naming';\n\nconst isQName = createValidator('qName', { xmlVersion: '1.0' });\n\nisQName('sku');   // false → regex test (cache miss), result cached\nisQName('sku');   // false → cache hit, no regex run\n```\n\nUse one instance per document/parse (or reuse across a session — your choice), rather\nthan creating one per call:\n\n```js\n// e.g. inside a parser, once per parse call:\nconst isValidTag = createValidator('qName', { asciiOnly: true });\n\nfor (const tagName of tagNames) {\n  if (!isValidTag(tagName)) throw new Error(`Invalid tag name: ${tagName}`);\n}\n```\n\nBecause the validator is a plain function, this loop already gets short-circuit\nbehaviour (via `break`/`throw` on first failure) and zero extra allocation on the happy\npath — no separate \"bulk\" API is needed for that.\n\n**Cache bound:** the internal cache is capped by `maxCacheSize` (default `2048`). Once\nthe cap is reached, new distinct strings are still validated correctly, they're just no\nlonger cached — existing cached entries keep being served. This keeps memory bounded\neven against high-cardinality or adversarial input (e.g. externally-supplied names that\nnever repeat), without the cost of a full LRU or the perf cliff of reset-and-refill.\n\nCall `.reset()` on the returned function to clear the cache manually, e.g. between\nunrelated parse calls if you're reusing one validator instance across a long-running\nprocess:\n\n```js\nisQName.reset();\n```\n\nThe cache is private to each `createValidator()` instance — there's no shared/global\ncache, so unrelated callers never interfere with each other.\n\n---\n\n### Diagnostic validation\n\n```js\nimport { validate } from 'xml-naming';\n\nvalidate('svg:circle', 'qName')\n// { valid: true, production: 'qName', input: 'svg:circle' }\n\nvalidate('1foo', 'ncName')\n// {\n//   valid: false,\n//   production: 'ncName',\n//   input: '1foo',\n//   reason: 'First character \"1\" is not a valid NameStartChar',\n//   position: 0\n// }\n\nvalidate('foo:bar', 'ncName')\n// {\n//   valid: false,\n//   production: 'ncName',\n//   input: 'foo:bar',\n//   reason: 'Colon is not allowed in NCName',\n//   position: 3\n// }\n\nvalidate('a:b:c', 'qName')\n// {\n//   valid: false,\n//   production: 'qName',\n//   input: 'a:b:c',\n//   reason: 'QName can have at most one colon',\n//   position: 3\n// }\n```\n\n---\n\n### Batch validation\n\n```js\nimport { validateAll } from 'xml-naming';\n\nvalidateAll(['svg', 'circle', '123bad', 'xlink:href'], 'ncName')\n// [\n//   { valid: true,  production: 'ncName', input: 'svg' },\n//   { valid: true,  production: 'ncName', input: 'circle' },\n//   { valid: false, production: 'ncName', input: '123bad',    reason: '...', position: 0 },\n//   { valid: false, production: 'ncName', input: 'xlink:href',reason: '...', position: 5 }\n// ]\n```\n\n---\n\n### Sanitize / auto-fix\n\nUseful when generating XML/SVG programmatically from user-supplied strings:\n\n```js\nimport { sanitize } from 'xml-naming';\n\nsanitize('123abc',    'ncName')  // '_123abc'   ← digit start fixed\nsanitize('my element','name')   // 'my_element' ← space replaced\nsanitize('foo:bar',   'ncName') // 'foobar'     ← colon stripped\nsanitize('hello!',    'name')   // 'hello_'     ← illegal char replaced\n\n// Custom replacement character\nsanitize('my element', 'name', { replacement: '-' })  // 'my-element'\n```\n\n---\n\n## Which production should I use?\n\n| Context | Production |\n|---|---|\n| XML element/attribute names (namespace-aware) | `qName` |\n| SVG `id` attribute values | `ncName` |\n| Namespace prefix alone | `ncName` |\n| DOCTYPE `<!ENTITY name ...>` | `name` |\n| DOCTYPE `<!NOTATION name ...>` | `name` |\n| DTD `NMTOKEN` attribute values | `nmToken` |\n| DTD `NMTOKENS` attribute values | `nmTokens` |\n\n> **Note:** DOCTYPE entity and notation names must use `Name`, not `QName`. Colons carry no namespace meaning in the DTD subset.\n\n---\n\n## API\n\n### `name(str, opts?)` → `boolean`\n### `ncName(str, opts?)` → `boolean`\n### `qName(str, opts?)` → `boolean`\n### `nmToken(str, opts?)` → `boolean`\n### `nmTokens(str, opts?)` → `boolean`\n\n`opts`:\n- `xmlVersion`: `'1.0'` (default) | `'1.1'`\n- `asciiOnly`: boolean (default `false`) — ASCII-only fast path, see above\n\n### `createValidator(production, opts?)` → memoized `(str) => boolean`, with `.reset()`\n\n`opts`:\n- `xmlVersion`: `'1.0'` (default) | `'1.1'`\n- `asciiOnly`: boolean (default `false`)\n- `maxCacheSize`: number (default `2048`) — cache stops accepting new entries once reached; existing entries keep serving hits\n\n### `validate(str, production, opts?)` → `ValidationResult`\n\n`production`: `'name'` | `'ncName'` | `'qName'` | `'nmToken'` | `'nmTokens'`\n\n`opts`: same as boolean validators (`xmlVersion`, `asciiOnly`)\n\n### `validateAll(strings[], production, opts?)` → `ValidationResult[]`\n\n`opts`: same as `validate`\n\n### `sanitize(str, production?, opts?)` → `string`\n\n`opts`:\n- `xmlVersion`: `'1.0'` | `'1.1'`\n- `replacement`: string (default `'_'`)\n- `asciiOnly`: boolean (default `false`) — also replaces non-ASCII characters, not just XML-illegal ones\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}