Comparing version 0.0.1 to 0.0.2
275
index.js
@@ -1,3 +0,2 @@ | ||
export function stringify(d){ | ||
function stringify(d){ | ||
// write the did: prefix | ||
@@ -44,12 +43,6 @@ var str = "did:" | ||
export function parse(input){ | ||
//p := &parser{input: input, out: &DID{}} | ||
function parse(input){ | ||
// the parser state machine is implemented as a loop over parser steps | ||
// steps increment p.currentIndex as they consume the input, each step returns the next step to run | ||
// the state machine halts when one of the steps returns nil | ||
// | ||
// This design is based on this talk from Rob Pike, although the talk focuses on lexical scanning, | ||
// the DID grammar is simple enough for us to combine lexing and parsing into one lexerless parse | ||
// http://www.youtube.com/watch?v=HxaD_trXwRE | ||
// steps increment currentIndex as they consume the input, each step returns the next step to run | ||
// the state machine halts when one of the steps returns 'done' | ||
try { | ||
@@ -65,5 +58,2 @@ var did = {} | ||
} | ||
// for parserState != nil { | ||
// parserState = parserState() | ||
// } | ||
} | ||
@@ -73,3 +63,3 @@ | ||
if (input.length < 7) { | ||
throw new Error('input length is less than 7: ' + inputLength) | ||
throw new Error('input length is less than 7: ' + input.length) | ||
} | ||
@@ -80,3 +70,3 @@ return parseScheme(input) | ||
function parseScheme(input) { | ||
var _did = { | ||
var d = { | ||
input: input, | ||
@@ -87,11 +77,11 @@ currentIndex: 3, | ||
// the grammar requires `did:` prefix | ||
if (_did.input.substring(0,_did.currentIndex+1) !== 'did:') { | ||
if (d.input.substring(0,d.currentIndex+1) !== 'did:') { | ||
throw new Error('input does not begin with did: prefix') | ||
} | ||
return parseMethod(_did) | ||
return parseMethod(d) | ||
} | ||
// extracts the DID Method | ||
function parseMethod(_did) { | ||
var currentIndex = _did.currentIndex + 1 | ||
function parseMethod(d) { | ||
var currentIndex = d.currentIndex + 1 | ||
var startIndex = currentIndex | ||
@@ -103,3 +93,3 @@ | ||
while (true) { | ||
if (_did.currentIndex === _did.input.length) { | ||
if (currentIndex === d.input.length) { | ||
// we got to the end of the input and didn't find a second ':' | ||
@@ -110,7 +100,7 @@ throw new Error('input does not have a second : marking end of method name') | ||
// read the input character at currentIndex | ||
var char = _did.input[currentIndex] | ||
var char = d.input[currentIndex] | ||
if (char === ':') { | ||
// we've found the second : in the input that marks the end of the method | ||
if (_did.currentIndex == startIndex) { | ||
if (currentIndex == startIndex) { | ||
// return error is method is empty, ex- did::1234 | ||
@@ -123,3 +113,3 @@ throw new Error('method is empty') | ||
// as per the grammar method can only be made of digits 0-9 or small letters a-z | ||
if (isNotDigit(char) && isNotSmallLetter(char)) { | ||
if (utils.isNotDigit(char) && utils.isNotSmallLetter(char)) { | ||
throw new Error('character is not a-z OR 0-9') | ||
@@ -133,7 +123,7 @@ } | ||
// set parser state | ||
_did.currentIndex = currentIndex | ||
_did.out.method = _did.input.substring(startIndex,currentIndex) | ||
d.currentIndex = currentIndex | ||
d.out.method = d.input.substring(startIndex,currentIndex) | ||
// method is followed by specific-idstring, parse that next | ||
return parseID(_did) | ||
return parseID(d) | ||
} | ||
@@ -143,5 +133,4 @@ | ||
// and adds them to out.IDStrings (later concatenated) | ||
function parseID(didIn) { | ||
let _did = didIn | ||
var currentIndex = _did.currentIndex + 1 | ||
function parseID(d) { | ||
var currentIndex = d.currentIndex + 1 | ||
var startIndex = currentIndex | ||
@@ -152,3 +141,3 @@ | ||
while (true) { | ||
if (currentIndex === _did.input.length) { | ||
if (currentIndex === d.input.length) { | ||
// we've reached end of input, no next state | ||
@@ -159,3 +148,3 @@ next = finish | ||
var char = _did.input[currentIndex] | ||
var char = d.input[currentIndex] | ||
@@ -182,3 +171,3 @@ if (char === ':') { | ||
// idchar = ALPHA / DIGIT / "." / "-" | ||
if (isNotValidIDChar(char)) { | ||
if (utils.isNotValidIDChar(char)) { | ||
throw new Error('byte is not ALPHA OR DIGIT OR . OR -') | ||
@@ -200,14 +189,13 @@ } | ||
// set parser state | ||
_did.currentIndex = currentIndex | ||
_did.out.idStrings = _did.out.idStrings || [] | ||
_did.out.idStrings = _did.out.idStrings.concat(_did.input.substring(startIndex,currentIndex)) | ||
d.currentIndex = currentIndex | ||
d.out.idStrings = d.out.idStrings || [] | ||
d.out.idStrings = d.out.idStrings.concat(d.input.substring(startIndex,currentIndex)) | ||
// return the next parser step | ||
return next(_did) | ||
return next(d) | ||
} | ||
// extracts a DID Path from a DID Reference | ||
function parsePath(didIn) { | ||
let _did = didIn | ||
var currentIndex = _did.currentIndex + 1 | ||
function parsePath(d) { | ||
var currentIndex = d.currentIndex + 1 | ||
var startIndex = currentIndex | ||
@@ -221,3 +209,3 @@ | ||
while (true) { | ||
if (currentIndex === _did.input.length) { | ||
if (currentIndex === d.input.length) { | ||
next = finish | ||
@@ -227,3 +215,3 @@ break | ||
var char = _did.input[currentIndex] | ||
var char = d.input[currentIndex] | ||
@@ -238,5 +226,5 @@ if (char === '/') { | ||
// a % must be followed by 2 hex digits | ||
if ((currentIndex+2 >= inputLength) || | ||
isNotHexDigit(_did.input[currentIndex+1]) || | ||
isNotHexDigit(_did.input[currentIndex+2])) { | ||
if ((currentIndex+2 >= d.input.length) || | ||
utils.isNotHexDigit(d.input[currentIndex+1]) || | ||
utils.isNotHexDigit(d.input[currentIndex+2])) { | ||
throw new Error('%% is not followed by 2 hex digits') | ||
@@ -254,3 +242,3 @@ } | ||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
if (!percentEncoded && isNotValidPathChar(char)) { | ||
if (!percentEncoded && utils.isNotValidPathChar(char)) { | ||
throw new Error('character is not allowed in path') | ||
@@ -263,3 +251,4 @@ } | ||
if (currentIndex == startIndex && _did.out.pathSegments && _did.out.pathSegments.length > 0) { | ||
d.out.pathSegments = d.out.pathSegments || [] | ||
if (currentIndex == startIndex && d.out.pathSegments.length === 0) { | ||
// path segment length is zero | ||
@@ -273,13 +262,11 @@ // first path segment must have atleast one character | ||
// update parser state | ||
_did.currentIndex = currentIndex | ||
_did.out.pathSegments = _did.out.pathSegments || [] | ||
_did.out.pathSegments = _did.out.pathSegments.concat(_did.input.substring(startIndex,currentIndex)) | ||
d.currentIndex = currentIndex | ||
d.out.pathSegments = d.out.pathSegments.concat(d.input.substring(startIndex,currentIndex)) | ||
return next(_did) | ||
return next(d) | ||
} | ||
// extracts a DID Fragment from a DID Reference | ||
function parseFragment(didIn) { | ||
let _did = didIn | ||
var currentIndex = _did.currentIndex + 1 | ||
function parseFragment(d) { | ||
var currentIndex = d.currentIndex + 1 | ||
var startIndex = currentIndex | ||
@@ -291,3 +278,3 @@ | ||
while (true) { | ||
if (currentIndex === _did.input.length) { | ||
if (currentIndex === d.input.length) { | ||
// we've reached the end of input | ||
@@ -299,9 +286,9 @@ // it's ok for reference to be empty, so we don't need a check for that | ||
var char = _did.input[currentIndex] | ||
var char = d.input[currentIndex] | ||
if (char === '%') { | ||
// a % must be followed by 2 hex digits | ||
if ((currentIndex+2 >= inputLength) || | ||
isNotHexDigit(input[currentIndex+1]) || | ||
isNotHexDigit(input[currentIndex+2])) { | ||
if ((currentIndex+2 >= d.input.length) || | ||
utils.isNotHexDigit(d.input[currentIndex+1]) || | ||
utils.isNotHexDigit(d.input[currentIndex+2])) { | ||
throw new Error('%% is not followed by 2 hex digits') | ||
@@ -320,4 +307,4 @@ } | ||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
// isNotValidFragmentChar checks for all othe valid chars except pct-encoded | ||
if (!percentEncoded && isNotValidFragmentChar(char)) { | ||
// utils.isNotValidFragmentChar checks for all othe valid chars except pct-encoded | ||
if (!percentEncoded && utils.isNotValidFragmentChar(char)) { | ||
throw new Error('character is not allowed in fragment ' + char) | ||
@@ -331,96 +318,106 @@ } | ||
// update parser state | ||
_did.currentIndex = currentIndex | ||
_did.out.fragment = _did.input.substring(startIndex,currentIndex) | ||
d.currentIndex = currentIndex | ||
d.out.fragment = d.input.substring(startIndex,currentIndex) | ||
// no more parsing needed after a fragment, | ||
// cause the state machine to exit by returning nil | ||
return finish(_did) | ||
return finish(d) | ||
} | ||
function finish(didIn){ | ||
var _did = didIn | ||
_did.out.idStrings = _did.out.idStrings || [] | ||
_did.out.id = _did.out.idStrings.join(':') | ||
_did.out.pathSegments = _did.out.pathSegments || [] | ||
_did.out.path = _did.out.pathSegments.join('/') | ||
_did.done = true | ||
return _did | ||
function finish(d){ | ||
d.out.idStrings = d.out.idStrings || [] | ||
d.out.id = d.out.idStrings.join(':') | ||
d.out.pathSegments = d.out.pathSegments || [] | ||
d.out.path = d.out.pathSegments.join('/') | ||
d.done = true | ||
return d | ||
} | ||
function isNotValidIDChar(char) { | ||
return isNotAlpha(char) && isNotDigit(char) && char != '.' && char != '-' | ||
} | ||
var utils = { | ||
// isNotValidFragmentChar returns true if a byte is not allowed in a Fragment | ||
// from the grammar: | ||
// did-fragment = *( pchar / "/" / "?" ) | ||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
// pct-encoded is not checked in this function | ||
function isNotValidFragmentChar(char) { | ||
return isNotValidPathChar(char) && char != '/' && char != '?' | ||
} | ||
isReference: function(did){ | ||
return (did.path || (did.pathSegments && did.pathSegments.length) || did.fragment) ? true : false | ||
}, | ||
// isNotValidPathChar returns true if a byte is not allowed in Path | ||
// did-path = segment-nz *( "/" segment ) | ||
// segment = *pchar | ||
// segment-nz = 1*pchar | ||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
// pct-encoded is not checked in this function | ||
function isNotValidPathChar(char) { | ||
return isNotUnreservedOrSubdelim(char) && char != ':' && char != '@' | ||
} | ||
isNotValidIDChar: function(char) { | ||
return utils.isNotAlpha(char) && utils.isNotDigit(char) && char != '.' && char != '-' | ||
}, | ||
// isNotUnreservedOrSubdelim returns true if a byte is not unreserved or sub-delims | ||
// from the grammar: | ||
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | ||
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" | ||
// https://tools.ietf.org/html/rfc3986#appendix-A | ||
function isNotUnreservedOrSubdelim(char) { | ||
if (['-', '.', '_', '~', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='].includes(char)){ | ||
return false | ||
} else { | ||
if (isNotAlpha(char) && isNotDigit(char)) { | ||
return true | ||
} | ||
return false | ||
} | ||
} | ||
// utils.isNotValidFragmentChar returns true if a byte is not allowed in a Fragment | ||
// from the grammar: | ||
// did-fragment = *( pchar / "/" / "?" ) | ||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
// pct-encoded is not checked in this function | ||
isNotValidFragmentChar: function(char) { | ||
return utils.isNotValidPathChar(char) && char != '/' && char != '?' | ||
}, | ||
// isNotHexDigit returns true if a byte is not a digit between 0-9 or A-F or a-f | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
function isNotHexDigit(char) { | ||
// '\x41' is A, '\x46' is F | ||
// '\x61' is a, '\x66' is f | ||
return isNotDigit(char) && (char < '\x41' || char > '\x46') && (char < '\x61' || char > '\x66') | ||
} | ||
// utils.isNotValidPathChar returns true if a byte is not allowed in Path | ||
// did-path = segment-nz *( "/" segment ) | ||
// segment = *pchar | ||
// segment-nz = 1*pchar | ||
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
// pct-encoded is not checked in this function | ||
isNotValidPathChar: function(char) { | ||
return utils.isNotUnreservedOrSubdelim(char) && char != ':' && char != '@' | ||
}, | ||
// isNotDigit returns true if a byte is not a digit between 0-9 | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
function isNotDigit(char) { | ||
// '\x30' is digit 0, '\x39' is digit 9 | ||
return (char < '\x30' || char > '\x39') | ||
} | ||
// utils.isNotUnreservedOrSubdelim returns true if a byte is not unreserved or sub-delims | ||
// from the grammar: | ||
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | ||
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" | ||
// https://tools.ietf.org/html/rfc3986#appendix-A | ||
isNotUnreservedOrSubdelim: function(char) { | ||
if (['-', '.', '_', '~', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='].includes(char)){ | ||
return false | ||
} else { | ||
if (utils.isNotAlpha(char) && utils.isNotDigit(char)) { | ||
return true | ||
} | ||
return false | ||
} | ||
}, | ||
// isNotAlpha returns true if a byte is not a big letter between A-Z or small letter between a-z | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
function isNotAlpha(char) { | ||
return isNotSmallLetter(char) && isNotBigLetter(char) | ||
} | ||
// utils.isNotHexDigit returns true if a byte is not a digit between 0-9 or A-F or a-f | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
isNotHexDigit: function(char) { | ||
// '\x41' is A, '\x46' is F | ||
// '\x61' is a, '\x66' is f | ||
return utils.isNotDigit(char) && (char < '\x41' || char > '\x46') && (char < '\x61' || char > '\x66') | ||
}, | ||
// isNotBigLetter returns true if a byte is not a big letter between A-Z | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
function isNotBigLetter(char) { | ||
// '\x41' is big letter A, '\x5A' small letter Z | ||
return (char < '\x41' || char > '\x5A') | ||
// utils.isNotDigit returns true if a byte is not a digit between 0-9 | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
isNotDigit: function(char) { | ||
// '\x30' is digit 0, '\x39' is digit 9 | ||
return (char < '\x30' || char > '\x39') | ||
}, | ||
// utils.isNotAlpha returns true if a byte is not a big letter between A-Z or small letter between a-z | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
isNotAlpha: function(char) { | ||
return utils.isNotSmallLetter(char) && utils.isNotBigLetter(char) | ||
}, | ||
// utils.isNotBigLetter returns true if a byte is not a big letter between A-Z | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
isNotBigLetter: function(char) { | ||
// '\x41' is big letter A, '\x5A' small letter Z | ||
return (char < '\x41' || char > '\x5A') | ||
}, | ||
// utils.isNotSmallLetter returns true if a byte is not a small letter between a-z | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
isNotSmallLetter: function(char) { | ||
// '\x61' is small letter a, '\x7A' small letter z | ||
return (char < '\x61' || char > '\x7A') | ||
} | ||
} | ||
// isNotSmallLetter returns true if a byte is not a small letter between a-z | ||
// in US-ASCII http://www.columbia.edu/kermit/ascii.html | ||
// https://tools.ietf.org/html/rfc5234#appendix-B.1 | ||
function isNotSmallLetter(char) { | ||
// '\x61' is small letter a, '\x7A' small letter z | ||
return (char < '\x61' || char > '\x7A') | ||
module.exports={ | ||
parse, stringify, utils | ||
} |
@@ -1,9 +0,20 @@ | ||
{ | ||
{ | ||
"name": "didjs", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "Decentralized Identifiers parser for javascript", | ||
"author": "Evan Feenstra <evan@evan.cool>", | ||
"main": "index.js", | ||
"keywords": ["decentralized", "identifier", "id"], | ||
"files": [ | ||
"index.js" | ||
], | ||
"keywords": [ | ||
"decentralized", | ||
"identifier", | ||
"id" | ||
], | ||
"scripts": { | ||
"test": "mocha" | ||
}, | ||
"devDependencies": { | ||
"mocha": "^6.1.4", | ||
"terser-webpack-plugin": "^1.3.0", | ||
@@ -10,0 +21,0 @@ "webpack": "^4.32.2" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
13449
3
2
337