Compact ES6 Fantom Name Service (FNS) Name Normalizer
npm install @0xftm/fns-normalize* Follows FNS Name Normalization Standard
* Passes 100% Validation Tests
* Custom NFC Implementation (or use native)
* Passes 100% Unicode 15.0.0 Normalization Tests
* Minified File Sizes:
* 27KB — native NFC via nf-native.js using String.normalize()
* 36KB Default — custom NFC via nf.js
42KB Everything!* — custom NFC + sub-libraries: parts.js, utils.js
* Included Apps:
* Resolver Demo
* Character Viewer
* Supported Emoji
* Confused Explainer
* External Apps:
* Recent Registrations
* Emoji Frequency Explorer
* FNS+NFT Matcher
* Batch Resolver
* Punycode Coder
* Breakdown Reports:
* Disallowed Characters
* Different Norm
* Illegal Mixtures
* Whole-script Confusables
* Illegal Placement
* Non-spacing Marks
``Javascript
import {ens_normalize} from '@adraffy/ens-normalize'; // or require()
// npm i @adraffy/ens-normalize
// browser: https://cdn.jsdelivr.net/npm/@adraffy/ens-normalize@latest/dist/index.min.js
// ALL errors thrown by this library are safe to print
// - characters are shown as {HEX} if should_escape()
// - potentially different bidi directions inside "quotes"
// - 200E is used near "quotes" to prevent spillover
// - an "error type" can be extracted by slicing up to the first (:)
// string -> string
// throws on invalid names
// output ready for namehash
let normalized = ens_normalize('RaFFY🚴♂️.eTh');
// => "raffy🚴♂.eth"
// note: does not enforce .eth TLD 3-character minimum
`
Format names with fully-qualified emoji:
`Javascript
// works like ens_normalize()
// output ready for display
let pretty = ens_beautify('1⃣2⃣.eth');
// => "1️⃣2️⃣.eth"
// note: normalization is unchanged:
// ens_normalize(ens_beautify(x)) == ens_normalize(x)
`
Normalize name fragments for substring search:
`Javascript`
// these fragments fail ens_normalize()
// but will normalize fine as fragments
let frag1 = ens_normalize_fragment('AB--'); // error: label ext
let frag2 = ens_normalize_fragment('\u{303}'); // error: leading cm
let frag3 = ens_normalize_fragment('οо'); // error: mixture
Input-based tokenization:
`Javascript
// string -> Token[]
// never throws
let tokens = ens_tokenize('_R💩\u{FE0F}a\u{FE0F}\u{304}\u{AD}./');
// [
// { type: 'valid', cp: [ 95 ] }, // valid (as-is)
// {
// type: 'mapped',
// cp: 82, // input
// cps: [ 114 ] // output
// },
// {
// type: 'emoji',
// input: Emoji(2) [ 128169, 65039 ], // input
// emoji: [ 128169, 65039 ], // fully-qualified
// cps: Emoji(1) [ 128169 ] // output (normalized)
// },
// {
// type: 'nfc',
// input: [ 97, 772 ], // input (before nfc)
// tokens0: [ // tokens (before nfc)
// { type: 'valid', cps: [ 97 ] },
// { type: 'ignored', cp: 65039 },
// { type: 'valid', cps: [ 772 ] }
// ],
// cps: [ 257 ], // output (after nfc)
// tokens: [ // tokens (after nfc)
// { type: 'valid', cps: [ 257 ] }
// ]
// },
// { type: 'ignored', cp: 173 },
// { type: 'stop', cp: 46 },
// { type: 'disallowed', cp: 47 }
// ]
// note: if name is normalizable, then:
// ens_normalize(ens_tokenize(name).map(token => {
// convert valid/mapped/nfc/stop to string
// }).join('')) == ens_normalize(name)
`
Output-based tokenization:
`Javascript`
// string -> Label[]
// never throws
let labels = ens_split('💩Raffy.eth_');
// [
// {
// input: [ 128169, 82, 97, 102, 102, 121 ],
// offset: 0, // index of codepoint, not substring index!
// // (corresponding length can be inferred from input)
// tokens: [
// Emoji(2) [ 128169, 65039 ], // emoji
// [ 114, 97, 102, 102, 121 ] // nfc-text
// ],
// output: [ 128169, 114, 97, 102, 102, 121 ],
// emoji: true,
// type: 'Latin'
// },
// {
// input: [ 101, 116, 104, 95 ],
// offset: 7,
// tokens: [ [ 101, 116, 104, 95 ] ],
// output: [ 101, 116, 104, 95 ],
// error: Error('underscore allowed only at start')
// }
// ]
Generate a sorted array of supported emoji codepoints:
`Javascript`
// () -> number[][]
console.log(ens_emoji());
// [
// [ 2764 ],
// [ 128169, 65039 ],
// [ 128105, 127997, 8205, 9877, 65039 ],
// ...
// ]
Determine if a character shouldn't be printed directly:
`Javascript`
// number -> bool
console.log(should_escape(0x202E)); // eg. RIGHT-TO-LEFT OVERRIDE => true
Determine if a character is a combining mark:
`Javascript`
// number -> bool
console.log(is_combining_mark(0x20E3)); // eg. COMBINING ENCLOSING KEYCAP => true
* git clone this repo, then npm install npm run derive
* Follow instructions in /derive/ to generate data files
* npm run make
* spec.json
* nf.json
* nf-tests.json
* — compress data files from /derive/output/npm run validate
* include-ens.js
* include-nf.js
* Follow instructions in /validate/ to generate validation tests
* npm run test
* tests.json
* — perform validation testsnpm run build
* — create /dist/npm run rebuild
* — run all the commands abovenpm run order` — create optimal group ordering and rebuild again
*