Skip to content

doga/IRI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

33 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Internationalized Resource Identifier

An IRI parser. In addition to a parser class, this library also provides a parser for tagged templates, which is used thusly:

This template parser has the same effect as IriParser.parse(), except that the former returns null in case of a parsing error, whereas the latter throws a TypeError.

---
title: Class diagram
---

classDiagram
  URL: string host
  URL: string pathname
  URL: toString()
  URL: ...

  IRL: URL url
  IRL: string host
  IRL: string pathname
  IRL: toString()
  IRL: ...
  IRL --* URL : has property
  note for IRL "𝘜𝘯π˜ͺ𝘀𝘰π˜₯𝘦 𝘳𝘦𝘱𝘳𝘦𝘴𝘦𝘯𝘡𝘒𝘡π˜ͺ𝘰𝘯 𝘰𝘧 𝘒 π˜œπ˜™π˜“."

  URN: string namespace
  URN: string namespaceSpecific
  URN: ...
  URN: toString()

  IriParser: parse()
  IriParser ..> IRL : depends on
  IriParser ..> URN : depends on
Loading

Usage examples

Tip: Run the examples below by typing this in your terminal (requires Deno 2+):

deno run \
  --allow-net --allow-run --allow-env --allow-read \
  jsr:@andrewbrey/[email protected] \
  --dax=false \
  https://raw.githubusercontent.com/doga/IRI/master/README.md
Parse IRIs using the parser class.
description = '''
Running this example is safe, it will not read or write anything to your filesystem.
'''
import { IriParser, URN } from 'https://esm.sh/gh/doga/[email protected]/mod.mjs';

const
iriStrings = [
  'https://çağlayan.info/user/çağlayan/?çağlayan#çağlayan',
  'urn:example:path?+resolver?=query#fragment',
  'url',
];

for (const iriString of iriStrings){
  console.info(`Parsing: ${iriString}`);
  try{
    const iri = IriParser.parse(iriString);

    if (iri instanceof URN) {
      console.info(`
        namespace         πŸ‘‰ ${iri.namespace}
        namespaceSpecific πŸ‘‰ ${iri.namespaceSpecific}
        resolver          πŸ‘‰ ${iri.resolver}
        query             πŸ‘‰ ${iri.query}
        fragment          πŸ‘‰ ${iri.fragment}
      `);
    } else { // IRL (URL with Unicode characters)
      console.info(`
      IRL:
        origin   πŸ‘‰ ${iri.origin}
        hostname πŸ‘‰ ${iri.hostname}
        host     πŸ‘‰ ${iri.host}
        pathname πŸ‘‰ ${iri.pathname}
        hash     πŸ‘‰ ${iri.hash}
        search   πŸ‘‰ ${iri.search}

      URL:
        origin   πŸ‘‰ ${iri.url.origin}
        hostname πŸ‘‰ ${iri.url.hostname}
        host     πŸ‘‰ ${iri.url.host}
        pathname πŸ‘‰ ${iri.url.pathname}
        hash     πŸ‘‰ ${iri.url.hash}
        search   πŸ‘‰ ${iri.url.search}
      `);
    }
  }catch(error){
    console.error(`${error}`);
  }
}

Sample output for the code above:

Parsing: https://çağlayan.info/user/çağlayan/?çağlayan#çağlayan

      IRL:
        origin   πŸ‘‰ https://Γ§ağlayan.info
        hostname πŸ‘‰ Γ§ağlayan.info
        host     πŸ‘‰ Γ§ağlayan.info
        pathname πŸ‘‰ /user/Γ§ağlayan/
        hash     πŸ‘‰ #Γ§ağlayan
        search   πŸ‘‰ ?Γ§ağlayan

      URL:
        origin   πŸ‘‰ https://xn--alayan-vua36b.info
        hostname πŸ‘‰ xn--alayan-vua36b.info
        host     πŸ‘‰ xn--alayan-vua36b.info
        pathname πŸ‘‰ /user/%C3%A7a%C4%9Flayan/
        hash     πŸ‘‰ #%C3%A7a%C4%9Flayan
        search   πŸ‘‰ ?%C3%A7a%C4%9Flayan

Parsing: urn:example:path?+resolver?=query#fragment

        namespace         πŸ‘‰ example
        namespaceSpecific πŸ‘‰ path
        resolver          πŸ‘‰ ?+resolver
        query             πŸ‘‰ ?=query
        fragment          πŸ‘‰ #fragment

Parsing: url
TypeError: Invalid IRI: 'url'
Parse IRIs using tagged templates.
description = '''
Running this example is safe, it will not read or write anything to your filesystem.
'''
import { IRI, URN } from 'https://esm.sh/gh/doga/[email protected]/mod.mjs';

const
iris = [
  IRI`https://çağlayan.info/user/çağlayan/?çağlayan#çağlayan`,
  IRI`urn:example:path?+resolver?=query#fragment`,
  IRI`url`, // === null
];

for (const iri of iris){
  if(!iri)continue;
  console.info(`IRI: ${iri}`);
  if (iri instanceof URN) {
    console.info(`
      namespace         πŸ‘‰ ${iri.namespace}
      namespaceSpecific πŸ‘‰ ${iri.namespaceSpecific}
      resolver          πŸ‘‰ ${iri.resolver}
      query             πŸ‘‰ ${iri.query}
      fragment          πŸ‘‰ ${iri.fragment}
    `);
  } else { // IRL (URL with Unicode characters)
    console.info(`
    IRL:
      origin   πŸ‘‰ ${iri.origin}
      hostname πŸ‘‰ ${iri.hostname}
      host     πŸ‘‰ ${iri.host}
      pathname πŸ‘‰ ${iri.pathname}
      hash     πŸ‘‰ ${iri.hash}
      search   πŸ‘‰ ${iri.search}

    URL:
      origin   πŸ‘‰ ${iri.url.origin}
      hostname πŸ‘‰ ${iri.url.hostname}
      host     πŸ‘‰ ${iri.url.host}
      pathname πŸ‘‰ ${iri.url.pathname}
      hash     πŸ‘‰ ${iri.url.hash}
      search   πŸ‘‰ ${iri.url.search}
    `);
  }
}

Sample output for the code above:

IRI: https://çağlayan.info/user/çağlayan/?çağlayan#çağlayan

    IRL:
      origin   πŸ‘‰ https://Γ§ağlayan.info
      hostname πŸ‘‰ Γ§ağlayan.info
      host     πŸ‘‰ Γ§ağlayan.info
      pathname πŸ‘‰ /user/Γ§ağlayan/
      hash     πŸ‘‰ #Γ§ağlayan
      search   πŸ‘‰ ?Γ§ağlayan

    URL:
      origin   πŸ‘‰ https://xn--alayan-vua36b.info
      hostname πŸ‘‰ xn--alayan-vua36b.info
      host     πŸ‘‰ xn--alayan-vua36b.info
      pathname πŸ‘‰ /user/%C3%A7a%C4%9Flayan/
      hash     πŸ‘‰ #%C3%A7a%C4%9Flayan
      search   πŸ‘‰ ?%C3%A7a%C4%9Flayan

IRI: urn:example:path?+resolver?=query#fragment

      namespace         πŸ‘‰ example
      namespaceSpecific πŸ‘‰ path
      resolver          πŸ‘‰ ?+resolver
      query             πŸ‘‰ ?=query
      fragment          πŸ‘‰ #fragment
Parse some URNs that belong to well-known and less well-known namespaces.
description = '''
Running this example is safe, it will not read or write anything to your filesystem.
'''
import { IriParser } from 'https://esm.sh/gh/doga/[email protected]/mod.mjs';

const
iriStrings = [
  'urn:isbn:0451450523',
  // 'urn:isan:0000-0000-2CEA-0000-1-0000-0000-Y',
  // 'urn:ISSN:0167-6423',
  'urn:ietf:rfc:2648',
  // 'urn:mpeg:mpeg7:schema:2001',
  // 'urn:oid:2.16.840',
  'urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66',
  // 'urn:nbn:de:bvb:19-146642',
  // 'urn:lex:eu:council:directive:2010-03-09;2010-19-UE',
  // 'urn:lsid:zoobank.org:pub:CDC8D258-8F57-41DC-B560-247E17D3DC8C',
  // 'urn:epc:class:lgtin:4012345.012345.998877',
  // 'urn:epc:id:sgtin:0614141.112345.400',
  // 'urn:epc:id:sscc:0614141.1234567890',
  // 'urn:epc:id:sgln:0614141.12345.400',
  // 'urn:epc:id:bic:CSQU3054383',
  // 'urn:epc:id:imovn:9176187',
  // 'urn:epc:id:gdti:0614141.12345.400',
  // 'urn:mrn:iala:aton:us:1234.5',
  // 'urn:mrn:iala:vts:ca:ecareg',
  // 'urn:mrn:iala:wwy:us:atl:chba:potri',
  // 'urn:mrn:iala:pub:g1143',
  // 'urn:microsoft:adfs:claimsxray',
  // 'urn:eic:10X1001A1001A450',
  'urn:rts:video:14795747', // https://www.rts.ch/play/tv/emissions
  'urn:li:share:7188778814934507520', // https://www.linkedin.com/feed/update/urn:li:share:7188778814934507520/
];

for (const iriString of iriStrings){
  try{
    const iri = IriParser.parse(iriString);
    console.info(`${iri}
      namespace         πŸ‘‰ ${iri.namespace}
      namespaceSpecific πŸ‘‰ ${iri.namespaceSpecific}
    `);
  }catch(error){
    console.error(error);
  }
}

Sample output for the code above:

urn:isbn:0451450523
      namespace         πŸ‘‰ isbn
      namespaceSpecific πŸ‘‰ 0451450523

urn:ietf:rfc:2648
      namespace         πŸ‘‰ ietf
      namespaceSpecific πŸ‘‰ rfc:2648

urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66
      namespace         πŸ‘‰ uuid
      namespaceSpecific πŸ‘‰ 6e8bc430-9c3a-11d9-9669-0800200c9a66

urn:rts:video:14795747
      namespace         πŸ‘‰ rts
      namespaceSpecific πŸ‘‰ video:14795747

urn:li:share:7188778814934507520
      namespace         πŸ‘‰ li
      namespaceSpecific πŸ‘‰ share:7188778814934507520

∎