npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@jackens/nnn

v2024.10.4

Published

Jackens’ JavaScript helpers.

Downloads

282

Readme

nnn

Jackens’ JavaScript helpers.

Version: 2024.10.4

Installation

bun i @jackens/nnn

or

npm i @jackens/nnn

Usage

import { «something» } from '@jackens/nnn'

or:

import { «something» } from './node_modules/@jackens/nnn/nnn.js'

or:

import { «something» } from 'https://unpkg.com/@jackens/[email protected]/nnn.js'

Exports

  • CNode: The type of arguments of the c helper.
  • CRoot: The type of arguments of the c helper.
  • EscapeMap: The type of arguments of the escapeValues and escape helpers.
  • HArgs: The type of arguments of the h and s helpers.
  • HArgs1: The type of arguments of the h and s helpers.
  • c: A simple JS-to-CSS (aka CSS-in-JS) helper.
  • csvParse: A tiny helper for CSV parsing.
  • escape: A generic helper for escaping values by given escapeMap (in TemplateStrings flavor).
  • escapeValues: A generic helper for escaping values by given escapeMap.
  • fixTypography: A helper that implements typographic corrections specific to Polish typography.
  • h: A lightweight HyperScript-style helper for creating and modifying HTMLElements (see also s).
  • has: A replacement for the in operator (not to be confused with the for-in loop) that works properly.
  • is: A helper that checks if the given argument is of a certain type.
  • jsOnParse: JSON.parse with “JavaScript turned on”.
  • locale: Language translations helper.
  • nanolight: A generic helper for syntax highlighting (see also nanolightJs).
  • nanolightJs: A helper for highlighting JavaScript (see also nanolight).
  • omit: A helper that implements TypeScript’s Omit utility type (see also pick).
  • pick: A helper that implements TypeScript’s Pick utility type (see also omit).
  • plUral: A helper for choosing the correct singular and plural.
  • pro: A helper that protects calls to nested properties by a Proxy that initializes non-existent values with an empty
  • refsInfo: A helper that provides information about the given refs.
  • s: A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).
  • svgUse: A convenient shortcut for s('svg', ['use', { 'xlink:href': '#' + id }], ...args).
  • uuid1: A helper that generates a UUID v1 identifier (with a creation timestamp).

CNode

type CNode = {
    [attributeOrSelector: string]: string | number | CNode | undefined;
};

The type of arguments of the c helper.

CRoot

type CRoot = Partial<Record<PropertyKey, CNode>>;

The type of arguments of the c helper.

EscapeMap

type EscapeMap = Map<unknown, (value?: unknown) => string>;

The type of arguments of the escapeValues and escape helpers.

HArgs

type HArgs = [string | Node, ...HArgs1[]];

The type of arguments of the h and s helpers.

HArgs1

type HArgs1 = Partial<Record<PropertyKey, unknown>> | null | undefined | Node | string | number | HArgs;

The type of arguments of the h and s helpers.

c

const c: (root: CRoot, splitter?: string) => string;

A simple JS-to-CSS (aka CSS-in-JS) helper.

The root parameter provides a hierarchical description of CSS rules.

  • Keys of sub-objects whose values are NOT objects are treated as CSS attribute, and values are treated as values of those CSS attributes; the concatenation of keys of all parent objects is a CSS rule.
  • All keys ignore the part starting with a splitter (default: $$) sign until the end of the key (e.g. src$$1src, @font-face$$1@font-face).
  • In keys specifying CSS attribute, all uppercase letters are replaced by lowercase letters with an additional - character preceding them (e.g. fontFamilyfont-family).
  • Commas in keys that makes a CSS rule cause it to “split” and create separate rules for each part (e.g. {div:{margin:1,'.a,.b,.c':{margin:2}}}div{margin:1}div.a,div.b,div.c{margin:2}).
  • Top-level keys that begin with @ are not concatenated with sub-object keys.

Usage Examples

const actual = c({
  a: {
    color: 'red',
    margin: 1,
    '.c': { margin: 2, padding: 2 },
    padding: 1
  }
})

const expected = `
a{
  color:red;
  margin:1
}
a.c{
  margin:2;
  padding:2
}
a{
  padding:1
}`.replace(/\n\s*/g, '')

expect(actual).to.deep.equal(expected)
const actual = c({
  a: {
    '.b': {
      color: 'red',
      margin: 1,
      '.c': { margin: 2, padding: 2 },
      padding: 1
    }
  }
})

const expected = `
a.b{
  color:red;
  margin:1
}
a.b.c{
  margin:2;
  padding:2
}
a.b{
  padding:1
}`.replace(/\n\s*/g, '')

expect(actual).to.deep.equal(expected)
const actual = c({
  '@font-face$$1': {
    fontFamily: 'Jackens',
    src$$1: 'url(otf/jackens.otf)',
    src$$2: "url(otf/jackens.otf) format('opentype')," +
      "url(svg/jackens.svg) format('svg')",
    fontWeight: 'normal',
    fontStyle: 'normal'
  },
  '@font-face$$2': {
    fontFamily: 'C64',
    src: 'url(fonts/C64_Pro_Mono-STYLE.woff)'
  },
  '@keyframes spin': {
    '0%': { transform: 'rotate(0deg)' },
    '100%': { transform: 'rotate(360deg)' }
  },
  div: {
    border: 'solid red 1px',
    '.c1': { 'background-color': '#000' },
    ' .c1': { backgroundColor: 'black' },
    '.c2': { backgroundColor: 'rgb(0,0,0)' }
  },
  '@media(min-width:200px)': {
    div: { margin: 0, padding: 0 },
    span: { color: '#000' }
  }
})

const expected = `
@font-face{
  font-family:Jackens;
  src:url(otf/jackens.otf);
  src:url(otf/jackens.otf) format('opentype'),url(svg/jackens.svg) format('svg');
  font-weight:normal;
  font-style:normal
}
@font-face{
  font-family:C64;
  src:url(fonts/C64_Pro_Mono-STYLE.woff)
}
@keyframes spin{
  0%{
    transform:rotate(0deg)
  }
  100%{
    transform:rotate(360deg)
  }
}
div{
  border:solid red 1px
}
div.c1{
  background-color:#000
}
div .c1{
  background-color:black
}
div.c2{
  background-color:rgb(0,0,0)
}
@media(min-width:200px){
  div{
    margin:0;
    padding:0
  }
  span{
    color:#000
  }
}`.replace(/\n\s*/g, '')

expect(actual).to.deep.equal(expected)
const actual = c({
  a: {
    '.b,.c': {
      margin: 1,
      '.d': {
        margin: 2
      }
    }
  }
})

const expected = `
a.b,a.c{
  margin:1
}
a.b.d,a.c.d{
  margin:2
}`.replace(/\n\s*/g, '')

expect(actual).to.deep.equal(expected)
const actual = c({
  '.b,.c': {
    margin: 1,
    '.d': {
      margin: 2
    }
  }
})

const expected = `
.b,.c{
  margin:1
}
.b.d,.c.d{
  margin:2
}`.replace(/\n\s*/g, '')

expect(actual).to.deep.equal(expected)
const actual = c({
  '.a,.b': {
    margin: 1,
    '.c,.d': {
      margin: 2
    }
  }
})

const expected = `
.a,.b{
  margin:1
}
.a.c,.a.d,.b.c,.b.d{
  margin:2
}`.replace(/\n\s*/g, '')

expect(actual).to.deep.equal(expected)

csvParse

const csvParse: {
    (text: string): Partial<Array<Partial<Record<PropertyKey, string>>>>;
    <HeaderTrue extends {
        header: true;
    }>(text: string, config: HeaderTrue): Partial<Array<Partial<Record<PropertyKey, string>>>>;
    <HeaderFalse extends {
        header: false;
    }>(text: string, config: HeaderFalse): Partial<Array<Partial<Array<string>>>>;
    (text: string, config: Partial<{
        header: boolean;
        separator: string;
    }>): Partial<Array<Partial<Record<PropertyKey, string>>>> | Partial<Array<Partial<Array<string>>>>;
};

A tiny helper for CSV parsing.

Options:

  • header: flag indicating that the parsed CSV has a header row (default: true)
  • separator: field separator (default: ',')

Usage Examples

const text = `"aaa
""aaa""
aaa",bbb, "ccc,ccc"
"xxx,xxx", "yyy
yyy",zzz
 42 , "42" , 17

`
expect(csvParse(text, { header: false })).to.deep.equal([
  ['aaa\n"aaa"\naaa', 'bbb', 'ccc,ccc'],
  ['xxx,xxx', 'yyy\nyyy', 'zzz'],
  [' 42 ', '42', ' 17']
])

expect(csvParse(text)).to.deep.equal([{
  'aaa\n"aaa"\naaa': 'xxx,xxx',
  bbb: 'yyy\nyyy',
  'ccc,ccc': 'zzz'
}, {
  'aaa\n"aaa"\naaa': ' 42 ',
  bbb: '42',
  'ccc,ccc': ' 17'
}])

escape

const escape: (escapeMap: EscapeMap, template: TemplateStringsArray, ...values: Partial<Array<unknown>>) => string;

A generic helper for escaping values by given escapeMap (in TemplateStrings flavor).

Usage Examples

const /** @type {EscapeMap} */ escapeMap = new Map([
  [undefined, () => 'NULL'],
  [Array, (/** @type {Partial<Array<unknown>>} */ values) => escapeValues(escapeMap, values).join(', ')],
  [Boolean, (/** @type {boolean} */ value) => `b'${+value}'`],
  [Date, (/** @type {Date} */ value) => `'${value.toISOString().replace(/^(.+)T(.+)\..*$/, '$1 $2')}'`],
  [Number, (/** @type {number} */ value) => `${value}`],
  [String, (/** @type {string} */ value) => `'${value.replace(/'/g, "''")}'`]
])

const sql = escape.bind(null, escapeMap)

const actual = sql`
  SELECT *
  FROM table_name
  WHERE column_name IN (${[true, null, undefined, 42, '42', "4'2", /42/, new Date(323325000000)]})`

const expected = `
  SELECT *
  FROM table_name
  WHERE column_name IN (b'1', NULL, NULL, 42, '42', '4''2', NULL, '1980-03-31 04:30:00')`

expect(actual).to.deep.equal(expected)

escapeValues

const escapeValues: (escapeMap: EscapeMap, values: Partial<Array<unknown>>) => Partial<Array<string>>;

A generic helper for escaping values by given escapeMap.

fixTypography

const fixTypography: (node: Node) => void;

A helper that implements typographic corrections specific to Polish typography.

Usage Examples

const p = h('p', 'Pchnąć w tę łódź jeża lub ośm skrzyń fig (zob. https://pl.wikipedia.org/wiki/Pangram).')

fixTypography(p)

expect(p.innerHTML).to.deep.equal(
  'Pchnąć <span style="white-space:nowrap">w </span>tę łódź jeża lub ośm skrzyń fig ' +
  '(zob. https://\u200Bpl.\u200Bwikipedia.\u200Borg/\u200Bwiki/\u200BPangram).')

h

const h: {
    <T extends keyof HTMLElementTagNameMap>(tag: T, ...args1: Partial<Array<HArgs1>>): HTMLElementTagNameMap[T];
    <N extends Node>(node: N, ...args1: Partial<Array<HArgs1>>): N;
    (tagOrNode: string | Node, ...args1: Partial<Array<HArgs1>>): Node;
};

A lightweight HyperScript-style helper for creating and modifying HTMLElements (see also s).

  • The first argument of type string specifies the tag of the element to be created.
  • The first argument of type Node specifies the element to be modified.
  • All other arguments of type Partial<Record<PropertyKey, unknown>> are mappings of attributes and properties. Keys starting with $ specify properties (without the leading $) to be set on the element being created or modified. (Note that $ is not a valid attribute name character.) All other keys specify attributes to be set by setAttribute. An attribute equal to false causes the attribute to be removed by removeAttribute.
  • All other arguments of type null or undefined are simply ignored.
  • All other arguments of type Node are appended to the element being created or modified.
  • All other arguments of type string/number are converted to Text nodes and appended to the element being created or modified.
  • All other arguments of type HArgs are passed to h and the results are appended to the element being created or modified.

Usage Examples

const b = h('b')

expect(b.outerHTML).to.deep.equal('<b></b>')

const i = h('i', 'text')

h(b, i)

expect(i.outerHTML).to.deep.equal('<i>text</i>')
expect(b.outerHTML).to.deep.equal('<b><i>text</i></b>')

h(i, { $className: 'some class' })

expect(i.outerHTML).to.deep.equal('<i class="some class">text</i>')
expect(b.outerHTML).to.deep.equal('<b><i class="some class">text</i></b>')
expect(h('span', 'text').outerHTML).to.deep.equal('<span>text</span>')
expect(h('span', { $innerText: 'text' }).outerHTML).to.deep.equal('<span>text</span>')
expect(h('div', { style: 'margin:0;padding:0' }).outerHTML)
  .to.deep.equal('<div style="margin:0;padding:0"></div>')
expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML)
  .to.deep.equal('<div style="margin: 0px; padding: 0px;"></div>')
expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML)
  .to.deep.equal('<div style="margin: 0px; padding: 0px;"></div>')
const input1 = h('input', { value: 42 })
const input2 = h('input', { $value: '42' })

expect(input1.value).to.deep.equal('42')
expect(input2.value).to.deep.equal('42')

expect(input1.outerHTML).to.deep.equal('<input value="42">')
expect(input2.outerHTML).to.deep.equal('<input>')

const checkbox1 = h('input', { type: 'checkbox', checked: true })
const checkbox2 = h('input', { type: 'checkbox', $checked: true })

expect(checkbox1.checked).to.be.true
expect(checkbox2.checked).to.be.true

expect(checkbox1.outerHTML).to.deep.equal('<input type="checkbox" checked="">')
expect(checkbox2.outerHTML).to.deep.equal('<input type="checkbox">')
const div = h('div')

expect(div.key).to.be.undefined

h(div, { $key: { one: 1 } })

expect(div.key).to.deep.equal({ one: 1 })

h(div, { $key: { two: 2 } })

expect(div.key).to.deep.equal({ one: 1, two: 2 })

has

const has: (key: unknown, ref: unknown) => boolean;

A replacement for the in operator (not to be confused with the for-in loop) that works properly.

Usage Examples

const obj = { key: 'K', null: 'N' }

expect('key' in obj).to.be.true
expect(has('key', obj)).to.be.true

expect('null' in obj).to.be.true
expect(has('null', obj)).to.be.true

expect(null in obj).to.be.true
expect(has(null, obj)).to.be.false

expect('toString' in obj).to.be.true
expect(has('toString', obj)).to.be.false
let typeError

try {
  'key' in null
} catch (error) {
  typeError = error
}

expect(typeError instanceof TypeError) // Cannot use 'in' operator to search for 'key' in null
expect(has('key', null)).to.be.false

is

const is: {
    (type: ArrayConstructor, arg: unknown): arg is Partial<Array<unknown>>;
    (type: BigIntConstructor, arg: unknown): arg is bigint;
    (type: BooleanConstructor, arg: unknown): arg is boolean;
    (type: NumberConstructor, arg: unknown): arg is number;
    (type: ObjectConstructor, arg: unknown): arg is Partial<Record<PropertyKey, unknown>>;
    (type: StringConstructor, arg: unknown): arg is string;
    (type: SymbolConstructor, arg: unknown): arg is symbol;
    (type: undefined, arg: unknown): arg is undefined | null;
    <T extends abstract new (...args: Partial<Array<any>>) => unknown>(type: T, arg: unknown): arg is InstanceType<T>;
};

A helper that checks if the given argument is of a certain type.

Usage Examples

expect(is(Number, 42)).to.be.true
expect(is(Number, Number(42))).to.be.true

expect(is(Number, new Number(42))).to.be.true
expect(is(Number, NaN)).to.be.true
expect(is(String, '42')).to.be.true
expect(is(String, String('42'))).to.be.true

expect(is(String, new String('42'))).to.be.true
expect(is(Symbol, Symbol('42'))).to.be.true
expect(is(Symbol, Object(Symbol('42')))).to.be.true
expect(is(undefined, undefined)).to.be.true
expect(is(undefined, null)).to.be.true
expect(is(Object, {})).to.be.true
expect(is(Array, [])).to.be.true
expect(is(RegExp, /42/)).to.be.true
expect(is(Date, new Date(42))).to.be.true
expect(is(Set, new Set(['42', 42]))).to.be.true
expect(is(Map, new Map([[{ j: 42 }, { J: '42' }], [{ c: 42 }, { C: '42' }]]))).to.be.true
const iz = (/** @type {unknown} */ type, /** @type {unknown} */ arg) =>
  ({}).toString.call(arg).slice(8, -1) === type?.name

class FooBar { }

expect(is(FooBar, new FooBar())).to.be.true
expect(iz(FooBar, new FooBar())).to.be.false

expect(is(Object, new FooBar())).to.be.false
expect(iz(Object, new FooBar())).to.be.true

const fakeFooBar = {}

fakeFooBar[Symbol.toStringTag] = FooBar.name

expect(is(FooBar, fakeFooBar)).to.be.false
expect(iz(FooBar, fakeFooBar)).to.be.true

expect(is(Object, fakeFooBar)).to.be.true
expect(iz(Object, fakeFooBar)).to.be.false
const num = 42
const str = '42'

expect(is(Number, num)).to.be.true

try {
  num.constructor = str.constructor
} catch { /* empty */ }

expect(is(Number, num)).to.be.true

jsOnParse

const jsOnParse: (handlers: Partial<Record<PropertyKey, Function>>, text: string) => any;

JSON.parse with “JavaScript turned on”.

Objects having exactly one property which is present in the handlers map, i.e. objects of the form:

{ "«handlerName»": [«params»] }

are replaced by the result of call

handlers['«handlerName»'](...«params»)

Usage Examples

const handlers = {
  $hello: (/** @type {string} */ name) => `Hello ${name}!`,
  $foo: () => 'bar'
}

const actual = jsOnParse(handlers, `[
  {
    "$hello": ["World"]
  },
  {
    "nested": {
      "$hello": ["nested World"]
    },
    "one": 1,
    "two": 2
  },
  {
    "$foo": []
  },
  {
    "$foo": ["The parent object does not have exactly one property!"],
    "one": 1,
    "two": 2
  }
]`)

const expected = [
  'Hello World!',
  {
    nested: 'Hello nested World!',
    one: 1,
    two: 2
  },
  'bar',
  {
    $foo: ['The parent object does not have exactly one property!'],
    one: 1,
    two: 2
  }
]

expect(actual).to.deep.equal(expected)

locale

const locale: (map: Partial<Record<PropertyKey, Partial<Record<PropertyKey, string>>>>, defaultVersion: string) => (text: string, version?: string) => string;

Language translations helper.

Usage Examples

const _ = locale({
  default: { Password: 'Hasło' },
  button: { Login: 'Zaloguj' }
}, 'default')

expect(_('Login')).to.deep.equal('Login')
expect(_('Password')).to.deep.equal('Hasło')

expect(_('Undefined text')).to.deep.equal('Undefined text')

expect(_('Login', 'button')).to.deep.equal('Zaloguj')

expect(_('Password', 'undefined_version')).to.deep.equal('Hasło')
expect(_('Undefined text', 'undefined_version')).to.deep.equal('Undefined text')

expect(_('toString')).to.deep.equal('toString')
expect(_('toString', 'undefined_version')).to.deep.equal('toString')

nanolight

const nanolight: (pattern: RegExp, highlighters: Partial<Array<(chunk: string, index: number) => HArgs1>>, code: string) => HArgs1[];

A generic helper for syntax highlighting (see also nanolightJs).

nanolightJs

const nanolightJs: (code: string) => HArgs1[];

A helper for highlighting JavaScript (see also nanolight).

Usage Examples

const codeJs = 'const answerToLifeTheUniverseAndEverything = 42'

expect(nanolightJs(codeJs)).to.deep.equal([
  ['span', { class: 'keyword' }, 'const'],
  ' ',
  ['span', { class: 'literal' }, 'answerToLifeTheUniverseAndEverything'],
  ' ',
  ['span', { class: 'operator' }, '='],
  ' ',
  ['span', { class: 'number' }, '42']
])

omit

const omit: <T extends Partial<Record<PropertyKey, unknown>>, K extends Array<keyof T>>(obj: Partial<Record<PropertyKey, unknown>>, keys: Partial<Array<unknown>>) => Omit<T, K[number]>;

A helper that implements TypeScript’s Omit utility type (see also pick).

Usage Examples

const obj = { a: 42, b: '42', c: 17 }

expect(omit(obj, ['c'])).to.deep.equal({ a: 42, b: '42' })

pick

const pick: <T extends Partial<Record<PropertyKey, unknown>>, K extends Array<keyof T>>(obj: Partial<Record<PropertyKey, unknown>>, keys: Partial<Array<unknown>>) => Pick<T, K[number]>;

A helper that implements TypeScript’s Pick utility type (see also omit).

Usage Examples

const obj = { a: 42, b: '42', c: 17 }

expect(pick(obj, ['a', 'b'])).to.deep.equal({ a: 42, b: '42' })

plUral

const plUral: (singular: string, plural2: string, plural5: string, value: number) => string;

A helper for choosing the correct singular and plural.

Usage Examples

const auto = plUral.bind(null, 'auto', 'auta', 'aut')

expect(auto(0)).to.deep.equal('aut')
expect(auto(1)).to.deep.equal('auto')
expect(auto(17)).to.deep.equal('aut')
expect(auto(42)).to.deep.equal('auta')

const car = plUral.bind(null, 'car', 'cars', 'cars')

expect(car(0)).to.deep.equal('cars')
expect(car(1)).to.deep.equal('car')
expect(car(17)).to.deep.equal('cars')
expect(car(42)).to.deep.equal('cars')

pro

const pro: (ref: unknown) => any;

A helper that protects calls to nested properties by a Proxy that initializes non-existent values with an empty object.

Usage Examples

const ref = {}

pro(ref).one.two[3][4] = 1234

expect(ref).to.deep.equal({ one: { two: { 3: { 4: 1234 } } } })

pro(ref).one.two.tree = 123

expect(ref).to.deep.equal({ one: { two: { 3: { 4: 1234 }, tree: 123 } } })

pro(ref).one.two = undefined

expect(ref).to.deep.equal({ one: { two: undefined } })

delete pro(ref).one.two

expect(ref).to.deep.equal({ one: {} })

pro(ref).one.two.three.four

expect(ref).to.deep.equal({ one: { two: { three: { four: {} } } } })

pro(ref).one.two.three.four = 1234

expect(ref).to.deep.equal({ one: { two: { three: { four: 1234 } } } })

refsInfo

const refsInfo: (...refs: Partial<Array<unknown>>) => Partial<Array<[string, string, Partial<Array<string>>]>>;

A helper that provides information about the given refs.

It returns an array of triples: [«name», «prototype-name», «array-of-own-property-names»].

Usage Examples

const info = refsInfo(Array, Function)

expect(info.find(item => item?.[0] === 'Array')?.[2]?.includes('length')).to.be.true
expect(info.find(item => item?.[0] === 'Function')?.[2]?.includes('length')).to.be.true
const browserFingerprint = () => {
  const refs = Object.getOwnPropertyNames(window).map(name => window[name])
  const info = refsInfo(...refs)
  const json = JSON.stringify(info)
  const hash = Array(32).fill(0)
  let j = 0

  for (let i = 0; i < json.length; i++) {
    let charCode = json.charCodeAt(i)

    while (charCode > 0) {
      hash[j] = hash[j] ^ (charCode & 15)
      charCode >>= 4
      j = (j + 1) & 31
    }
  }

  return hash.map(x => x.toString(16)).join('')
}

console.log(browserFingerprint())

s

const s: {
    <T extends keyof SVGElementTagNameMap>(tag: T, ...args1: Partial<Array<HArgs1>>): SVGElementTagNameMap[T];
    <N extends Node>(node: N, ...args1: Partial<Array<HArgs1>>): N;
    (tagOrNode: string | Node, ...args1: Partial<Array<HArgs1>>): Node;
};

A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).

  • The first argument of type string specifies the tag of the element to be created.
  • The first argument of type Node specifies the element to be modified.
  • All other arguments of type Partial<Record<PropertyKey, unknown>> are mappings of attributes and properties. Keys starting with $ specify properties (without the leading $) to be set on the element being created or modified. (Note that $ is not a valid attribute name character.) All other keys specify attributes to be set by setAttributeNS. An attribute equal to false causes the attribute to be removed by removeAttributeNS.
  • All other arguments of type null or undefined are simply ignored.
  • All other arguments of type Node are appended to the element being created or modified.
  • All other arguments of type string/number are converted to Text nodes and appended to the element being created or modified.
  • All other arguments of type HArgs are passed to s and the results are appended to the element being created or modified.

svgUse

const svgUse: (id: string, ...args: Partial<Array<HArgs1>>) => SVGSVGElement;

A convenient shortcut for s('svg', ['use', { 'xlink:href': '#' + id }], ...args).

uuid1

const uuid1: ({ date, node }?: {
    date?: Date | undefined;
    node?: string | undefined;
}) => string;

A helper that generates a UUID v1 identifier (with a creation timestamp).

  • The optional node parameter should have the format /^[0123456789abcdef]+$/. Its value will be trimmed to last 12 characters and left padded with zeros.

Usage Examples

for (let i = 1; i <= 22136; ++i) {
  const uuid = uuid1()

  i === 1 && expect(uuid.split('-')[3]).to.deep.equal('8001')
  i === 4095 && expect(uuid.split('-')[3]).to.deep.equal('8fff')
  i === 4096 && expect(uuid.split('-')[3]).to.deep.equal('9000')
  i === 9029 && expect(uuid.split('-')[3]).to.deep.equal('a345')
  i === 13398 && expect(uuid.split('-')[3]).to.deep.equal('b456')
  i === 16384 && expect(uuid.split('-')[3]).to.deep.equal('8000')
  i === 17767 && expect(uuid.split('-')[3]).to.deep.equal('8567')
}
expect(uuid1({ node: '000123456789abc' }).split('-')[4]).to.deep.equal('123456789abc')
expect(uuid1({ node: '123456789' }).split('-')[4]).to.deep.equal('000123456789')
expect(uuid1({ date: new Date(323325000000) }).startsWith('c1399400-9a71-11bd')).to.be.true

Why Partial<Array> and Partial<Record>

Consider the following code snippet:

const arr = ['one', 'two', 'three'] // type: string[]
const arrAt42 = arr[42] // type: string

arrAt42.toUpperCase() // 👎 allowed by TypeScript

TypeScript allows arrAt42.toUpperCase(), which causes a TypeError: undefined is not an object. The variable arr should be of type Partial<Array<string>>:

const arr: Partial<Array<string>> = ['one', 'two', 'three']
const arrAt42 = arr[42] // type: string | undefined

arrAt42.toUpperCase()  // 👍 forbidden by TypeScript

Now arrAt42.toUpperCase() is forbidden by TypeScript ('arrAt42' is possibly undefined).

Similarly for the type Record:

const rec = Object.fromEntries(['one', 'two', 'three'].map((k, i) => [k, i])) // type: Record<string, number>
const { tree } = rec // type: number

tree.toFixed() // 👎 allowed by TypeScript

TypeScript allows tree.toFixed(), which causes a TypeError: undefined is not an object. The variable rec should be of type Partial<Record<PropertyKey, number>>:

const rec: Partial<Record<PropertyKey, number>> =
  Object.fromEntries(['one', 'two', 'three'].map((k, i) => [k, i]))
const { tree } = rec // type: number | undefined

tree.toFixed() // 👍 forbidden by TypeScript

Now tree.toFixed() is forbidden by TypeScript ('tree' is possibly undefined).

License

The MIT License (MIT)

Copyright (c) 2016+ Jackens

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.