structured-headers
Advanced tools
Comparing version 1.0.1 to 2.0.0-alpha.0
@@ -1,2 +0,2 @@ | ||
var structuredHeader;!function(){"use strict";var e={590:function(e,t,i){var r=this&&this.__createBinding||(Object.create?function(e,t,i,r){void 0===r&&(r=i);var s=Object.getOwnPropertyDescriptor(t,i);s&&!("get"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,r,s)}:function(e,t,i,r){void 0===r&&(r=i),e[r]=t[i]}),s=this&&this.__exportStar||function(e,t){for(var i in e)"default"===i||Object.prototype.hasOwnProperty.call(t,i)||r(t,e,i)};Object.defineProperty(t,"__esModule",{value:!0}),t.Token=void 0,s(i(223),t),s(i(759),t),s(i(985),t),s(i(128),t);var n=i(0);Object.defineProperty(t,"Token",{enumerable:!0,get:function(){return n.Token}})},759:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.ParseError=t.parseItem=t.parseList=t.parseDictionary=void 0;const r=i(985),s=i(0),n=i(128);t.parseDictionary=function(e){return new a(e).parseDictionary()},t.parseList=function(e){return new a(e).parseList()},t.parseItem=function(e){return new a(e).parseItem()};class o extends Error{constructor(e,t){super(`Parse error: ${t} at offset ${e}`)}}t.ParseError=o;class a{constructor(e){this.input=e,this.pos=0}parseDictionary(){this.skipWS();const e=new Map;for(;!this.eof();){const t=this.parseKey();let i;if("="===this.lookChar()?(this.pos++,i=this.parseItemOrInnerList()):i=[!0,this.parseParameters()],e.set(t,i),this.skipOWS(),this.eof())return e;if(this.expectChar(","),this.pos++,this.skipOWS(),this.eof())throw new o(this.pos,"Dictionary contained a trailing comma")}return e}parseList(){this.skipWS();const e=[];for(;!this.eof();){if(e.push(this.parseItemOrInnerList()),this.skipOWS(),this.eof())return e;if(this.expectChar(","),this.pos++,this.skipOWS(),this.eof())throw new o(this.pos,"A list may not end with a trailing comma")}return e}parseItem(e=!0){e&&this.skipWS();const t=[this.parseBareItem(),this.parseParameters()];return e&&this.checkTrail(),t}parseItemOrInnerList(){return"("===this.lookChar()?this.parseInnerList():this.parseItem(!1)}parseInnerList(){this.expectChar("("),this.pos++;const e=[];for(;!this.eof();){if(this.skipWS(),")"===this.lookChar())return this.pos++,[e,this.parseParameters()];e.push(this.parseItem(!1));const t=this.lookChar();if(" "!==t&&")"!==t)throw new o(this.pos,"Expected a whitespace or ) after every item in an inner list")}throw new o(this.pos,"Could not find end of inner list")}parseBareItem(){const e=this.lookChar();if(void 0===e)throw new o(this.pos,"Unexpected end of string");if(e.match(/^[-0-9]/))return this.parseIntegerOrDecimal();if('"'===e)return this.parseString();if(e.match(/^[A-Za-z*]/))return this.parseToken();if(":"===e)return this.parseByteSequence();if("?"===e)return this.parseBoolean();throw new o(this.pos,"Unexpected input")}parseParameters(){const e=new Map;for(;!this.eof()&&";"===this.lookChar();){this.pos++,this.skipWS();const t=this.parseKey();let i=!0;"="===this.lookChar()&&(this.pos++,i=this.parseBareItem()),e.set(t,i)}return e}parseIntegerOrDecimal(){let e="integer",t=1,i="";if("-"===this.lookChar()&&(t=-1,this.pos++),!c(this.lookChar()))throw new o(this.pos,"Expected a digit (0-9)");for(;!this.eof();){const t=this.getChar();if(c(t))i+=t;else{if("integer"!==e||"."!==t){this.pos--;break}if(i.length>12)throw new o(this.pos,"Exceeded maximum decimal length");i+=".",e="decimal"}if("integer"===e&&i.length>15)throw new o(this.pos,"Exceeded maximum integer length");if("decimal"===e&&i.length>16)throw new o(this.pos,"Exceeded maximum decimal length")}if("integer"===e)return parseInt(i,10)*t;if(i.endsWith("."))throw new o(this.pos,"Decimal cannot end on a period");if(i.split(".")[1].length>3)throw new o(this.pos,"Number of digits after the decimal point cannot exceed 3");return parseFloat(i)*t}parseString(){let e="";for(this.expectChar('"'),this.pos++;!this.eof();){const t=this.getChar();if("\\"===t){if(this.eof())throw new o(this.pos,"Unexpected end of input");const t=this.getChar();if("\\"!==t&&'"'!==t)throw new o(this.pos,"A backslash must be followed by another backslash or double quote");e+=t}else{if('"'===t)return e;if(!(0,n.isAscii)(t))throw new o(this.pos,"Strings must be in the ASCII range");e+=t}}throw new o(this.pos,"Unexpected end of input")}parseToken(){let e="";for(;!this.eof();){const t=this.lookChar();if(void 0===t||!/^[:/!#$%&'*+\-.^_`|~A-Za-z0-9]$/.test(t))return new s.Token(e);e+=this.getChar()}return new s.Token(e)}parseByteSequence(){this.expectChar(":"),this.pos++;const e=this.input.indexOf(":",this.pos);if(-1===e)throw new o(this.pos,'Could not find a closing ":" character to mark end of Byte Sequence');const t=this.input.substring(this.pos,e);if(this.pos+=t.length+1,!/^[A-Za-z0-9+/=]*$/.test(t))throw new o(this.pos,"ByteSequence does not contain a valid base64 string");return new r.ByteSequence(t)}parseBoolean(){this.expectChar("?"),this.pos++;const e=this.getChar();if("1"===e)return!0;if("0"===e)return!1;throw new o(this.pos,'Unexpected character. Expected a "1" or a "0"')}parseKey(){var e;if(!(null===(e=this.lookChar())||void 0===e?void 0:e.match(/^[a-z*]/)))throw new o(this.pos,"A key must begin with an asterisk or letter (a-z)");let t="";for(;!this.eof();){const e=this.lookChar();if(void 0===e||!/^[a-z0-9_\-.*]$/.test(e))return t;t+=this.getChar()}return t}lookChar(){return this.input[this.pos]}expectChar(e){if(this.lookChar()!==e)throw new o(this.pos,`Expected ${e}`)}getChar(){return this.input[this.pos++]}eof(){return this.pos>=this.input.length}skipOWS(){for(;;){const e=this.input.substr(this.pos,1);if(" "!==e&&"\t"!==e)break;this.pos++}}skipWS(){for(;" "===this.lookChar();)this.pos++}checkTrail(){if(this.skipWS(),!this.eof())throw new o(this.pos,"Unexpected characters at end of input")}}t.default=a;const h=/^[0-9]$/;function c(e){return void 0!==e&&h.test(e)}},223:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.serializeKey=t.serializeParameters=t.serializeToken=t.serializeByteSequence=t.serializeBoolean=t.serializeString=t.serializeDecimal=t.serializeInteger=t.serializeBareItem=t.serializeInnerList=t.serializeItem=t.serializeDictionary=t.serializeList=t.SerializeError=void 0;const r=i(985),s=i(0),n=i(128);class o extends Error{}function a(e){return c(e[0])+m(e[1])}function h(e){return`(${e[0].map((e=>a(e))).join(" ")})${m(e[1])}`}function c(e){if("number"==typeof e)return Number.isInteger(e)?u(e):p(e);if("string"==typeof e)return l(e);if(e instanceof s.Token)return w(e);if(e instanceof r.ByteSequence)return d(e);if("boolean"==typeof e)return f(e);throw new o("Cannot serialize values of type "+typeof e)}function u(e){if(e<-999999999999999||e>999999999999999)throw new o("Structured headers can only encode integers in the range range of -999,999,999,999,999 to 999,999,999,999,999 inclusive");return e.toString()}function p(e){const t=e.toFixed(3).replace(/0+$/,"");if(t.split(".")[0].replace("-","").length>12)throw new o("Fractional numbers are not allowed to have more than 12 significant digits before the decimal point");return t}function l(e){if(!(0,n.isAscii)(e))throw new o("Only ASCII strings may be serialized");return`"${e.replace(/("|\\)/g,(e=>"\\"+e))}"`}function f(e){return e?"?1":"?0"}function d(e){return`:${e.toBase64()}:`}function w(e){return e.toString()}function m(e){return Array.from(e).map((([e,t])=>{let i=";"+g(e);return!0!==t&&(i+="="+c(t)),i})).join("")}function g(e){if(!(0,n.isValidKeyStr)(e))throw new o("Keys in dictionaries must only contain lowercase letter, numbers, _-*. and must start with a letter or *");return e}t.SerializeError=o,t.serializeList=function(e){return e.map((e=>(0,n.isInnerList)(e)?h(e):a(e))).join(", ")},t.serializeDictionary=function(e){return Array.from(e.entries()).map((([e,t])=>{let i=g(e);return!0===t[0]?i+=m(t[1]):(i+="=",(0,n.isInnerList)(t)?i+=h(t):i+=a(t)),i})).join(", ")},t.serializeItem=a,t.serializeInnerList=h,t.serializeBareItem=c,t.serializeInteger=u,t.serializeDecimal=p,t.serializeString=l,t.serializeBoolean=f,t.serializeByteSequence=d,t.serializeToken=w,t.serializeParameters=m,t.serializeKey=g},0:function(e,t,i){Object.defineProperty(t,"__esModule",{value:!0}),t.Token=void 0;const r=i(128);t.Token=class{constructor(e){if(!(0,r.isValidTokenStr)(e))throw new TypeError("Invalid character in Token string. Tokens must start with *, A-Z and the rest of the string may only contain a-z, A-Z, 0-9, :/!#$%&'*+-.^_`|~");this.value=e}toString(){return this.value}}},985:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ByteSequence=void 0,t.ByteSequence=class{constructor(e){this.base64Value=e}toBase64(){return this.base64Value}}},128:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.isByteSequence=t.isInnerList=t.isValidKeyStr=t.isValidTokenStr=t.isAscii=void 0;const i=/^[\x20-\x7E]*$/,r=/^[a-zA-Z*][:/!#$%&'*+\-.^_`|~A-Za-z0-9]*$/,s=/^[a-z*][*\-_.a-z0-9]*$/;t.isAscii=function(e){return i.test(e)},t.isValidTokenStr=function(e){return r.test(e)},t.isValidKeyStr=function(e){return s.test(e)},t.isInnerList=function(e){return Array.isArray(e[0])},t.isByteSequence=function(e){return"object"==typeof e&&"base64Value"in e}}},t={},i=function i(r){var s=t[r];if(void 0!==s)return s.exports;var n=t[r]={exports:{}};return e[r].call(n.exports,n,n.exports,i),n.exports}(590);structuredHeader=i}(); | ||
var structuredHeader;!function(){"use strict";var e={901:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.DisplayString=void 0,t.DisplayString=class{constructor(e){this.value=e}toString(){return this.value}}},590:function(e,t,r){var i=this&&this.__createBinding||(Object.create?function(e,t,r,i){void 0===i&&(i=r);var s=Object.getOwnPropertyDescriptor(t,r);s&&!("get"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,i,s)}:function(e,t,r,i){void 0===i&&(i=r),e[i]=t[r]}),s=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||i(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.DisplayString=t.Token=void 0,s(r(223),t),s(r(759),t),s(r(985),t),s(r(128),t);var n=r(0);Object.defineProperty(t,"Token",{enumerable:!0,get:function(){return n.Token}});var o=r(901);Object.defineProperty(t,"DisplayString",{enumerable:!0,get:function(){return o.DisplayString}})},759:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.ParseError=t.parseItem=t.parseList=t.parseDictionary=void 0;const i=r(985),s=r(0),n=r(128),o=r(901);t.parseDictionary=function(e){return new h(e).parseDictionary()},t.parseList=function(e){return new h(e).parseList()},t.parseItem=function(e){return new h(e).parseItem()};class a extends Error{constructor(e,t){super(`Parse error: ${t} at offset ${e}`)}}t.ParseError=a;class h{constructor(e){this.input=e,this.pos=0}parseDictionary(){this.skipWS();const e=new Map;for(;!this.eof();){const t=this.parseKey();let r;if("="===this.lookChar()?(this.pos++,r=this.parseItemOrInnerList()):r=[!0,this.parseParameters()],e.set(t,r),this.skipOWS(),this.eof())return e;if(this.expectChar(","),this.pos++,this.skipOWS(),this.eof())throw new a(this.pos,"Dictionary contained a trailing comma")}return e}parseList(){this.skipWS();const e=[];for(;!this.eof();){if(e.push(this.parseItemOrInnerList()),this.skipOWS(),this.eof())return e;if(this.expectChar(","),this.pos++,this.skipOWS(),this.eof())throw new a(this.pos,"A list may not end with a trailing comma")}return e}parseItem(e=!0){e&&this.skipWS();const t=[this.parseBareItem(),this.parseParameters()];return e&&this.checkTrail(),t}parseItemOrInnerList(){return"("===this.lookChar()?this.parseInnerList():this.parseItem(!1)}parseInnerList(){this.expectChar("("),this.pos++;const e=[];for(;!this.eof();){if(this.skipWS(),")"===this.lookChar())return this.pos++,[e,this.parseParameters()];e.push(this.parseItem(!1));const t=this.lookChar();if(" "!==t&&")"!==t)throw new a(this.pos,"Expected a whitespace or ) after every item in an inner list")}throw new a(this.pos,"Could not find end of inner list")}parseBareItem(){const e=this.lookChar();if(void 0===e)throw new a(this.pos,"Unexpected end of string");if(e.match(/^[-0-9]/))return this.parseIntegerOrDecimal();if('"'===e)return this.parseString();if(e.match(/^[A-Za-z*]/))return this.parseToken();if(":"===e)return this.parseByteSequence();if("?"===e)return this.parseBoolean();if("@"===e)return this.parseDate();if("%"===e)return this.parseDisplayString();throw new a(this.pos,"Unexpected input")}parseParameters(){const e=new Map;for(;!this.eof()&&";"===this.lookChar();){this.pos++,this.skipWS();const t=this.parseKey();let r=!0;"="===this.lookChar()&&(this.pos++,r=this.parseBareItem()),e.set(t,r)}return e}parseIntegerOrDecimal(){let e="integer",t=1,r="";if("-"===this.lookChar()&&(t=-1,this.pos++),!p(this.lookChar()))throw new a(this.pos,"Expected a digit (0-9)");for(;!this.eof();){const t=this.getChar();if(p(t))r+=t;else{if("integer"!==e||"."!==t){this.pos--;break}if(r.length>12)throw new a(this.pos,"Exceeded maximum decimal length");r+=".",e="decimal"}if("integer"===e&&r.length>15)throw new a(this.pos,"Exceeded maximum integer length");if("decimal"===e&&r.length>16)throw new a(this.pos,"Exceeded maximum decimal length")}if("integer"===e)return parseInt(r,10)*t;if(r.endsWith("."))throw new a(this.pos,"Decimal cannot end on a period");if(r.split(".")[1].length>3)throw new a(this.pos,"Number of digits after the decimal point cannot exceed 3");return parseFloat(r)*t}parseString(){let e="";for(this.expectChar('"'),this.pos++;!this.eof();){const t=this.getChar();if("\\"===t){if(this.eof())throw new a(this.pos,"Unexpected end of input");const t=this.getChar();if("\\"!==t&&'"'!==t)throw new a(this.pos,"A backslash must be followed by another backslash or double quote");e+=t}else{if('"'===t)return e;if(!(0,n.isAscii)(t))throw new a(this.pos,"Strings must be in the ASCII range");e+=t}}throw new a(this.pos,"Unexpected end of input")}parseDisplayString(){if('%"'!==this.getChars(2))throw new a(this.pos,"Unexpected character. Display strings should start with %=");const e=[];for(;!this.eof();){const t=this.getChar();if(t.charCodeAt(0)<=31||t.charCodeAt(0)>=127&&t.charCodeAt(0)<=255)throw new a(this.pos,"Invalid char found in DisplayString. Did you forget to escape?");if("%"!==t){if('"'===t){const t=new TextDecoder("utf-8",{fatal:!0});try{return new o.DisplayString(t.decode(new Uint8Array(e)))}catch(e){throw new a(this.pos,"Fatal error decoding UTF-8 sequence in Display String")}}e.push(t.charCodeAt(0))}else{const t=this.getChars(2);if(!/^[0-9a-f]{2}$/.test(t))throw new a(this.pos,`Unexpected sequence after % in DispalyString: "${t}". Note that hexidecimals must be lowercase`);e.push(parseInt(t,16))}}throw new a(this.pos,"Unexpected end of input")}parseToken(){let e="";for(;!this.eof();){const t=this.lookChar();if(void 0===t||!/^[:/!#$%&'*+\-.^_`|~A-Za-z0-9]$/.test(t))return new s.Token(e);e+=this.getChar()}return new s.Token(e)}parseByteSequence(){this.expectChar(":"),this.pos++;const e=this.input.indexOf(":",this.pos);if(-1===e)throw new a(this.pos,'Could not find a closing ":" character to mark end of Byte Sequence');const t=this.input.substring(this.pos,e);if(this.pos+=t.length+1,!/^[A-Za-z0-9+/=]*$/.test(t))throw new a(this.pos,"ByteSequence does not contain a valid base64 string");return new i.ByteSequence(t)}parseBoolean(){this.expectChar("?"),this.pos++;const e=this.getChar();if("1"===e)return!0;if("0"===e)return!1;throw new a(this.pos,'Unexpected character. Expected a "1" or a "0"')}parseDate(){this.expectChar("@"),this.pos++;let e=1,t="";if("-"===this.lookChar()&&(e=-1,this.pos++),!p(this.lookChar()))throw new a(this.pos,"Expected a digit (0-9)");for(;!this.eof();){const e=this.getChar();if(!p(e))throw new a(this.pos,"Expected a digit (0-9), whitespace or EOL");t+=e}return new Date(parseInt(t,10)*e*1e3)}parseKey(){var e;if(!(null===(e=this.lookChar())||void 0===e?void 0:e.match(/^[a-z*]/)))throw new a(this.pos,"A key must begin with an asterisk or letter (a-z)");let t="";for(;!this.eof();){const e=this.lookChar();if(void 0===e||!/^[a-z0-9_\-.*]$/.test(e))return t;t+=this.getChar()}return t}lookChar(){return this.input[this.pos]}expectChar(e){if(this.lookChar()!==e)throw new a(this.pos,`Expected ${e}`)}getChar(){return this.input[this.pos++]}getChars(e){const t=this.input.substr(this.pos,e);return this.pos+=e,t}eof(){return this.pos>=this.input.length}skipOWS(){for(;;){const e=this.input.substr(this.pos,1);if(" "!==e&&"\t"!==e)break;this.pos++}}skipWS(){for(;" "===this.lookChar();)this.pos++}checkTrail(){if(this.skipWS(),!this.eof())throw new a(this.pos,"Unexpected characters at end of input")}}t.default=h;const c=/^[0-9]$/;function p(e){return void 0!==e&&c.test(e)}},223:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.serializeKey=t.serializeParameters=t.serializeDate=t.serializeToken=t.serializeByteSequence=t.serializeBoolean=t.serializeDisplayString=t.serializeString=t.serializeDecimal=t.serializeInteger=t.serializeBareItem=t.serializeInnerList=t.serializeItem=t.serializeDictionary=t.serializeList=t.SerializeError=void 0;const i=r(985),s=r(0),n=r(128),o=r(901);class a extends Error{}function h(e,t){return Array.isArray(e)?p(e[0])+S(e[1]):p(e)+(t?S(t):"")}function c(e){return`(${e[0].map((e=>h(e))).join(" ")})${S(e[1])}`}function p(e){if("number"==typeof e)return Number.isInteger(e)?u(e):l(e);if("string"==typeof e)return f(e);if(e instanceof s.Token)return y(e);if(e instanceof i.ByteSequence)return g(e);if(e instanceof o.DisplayString)return d(e);if(e instanceof Date)return m(e);if("boolean"==typeof e)return w(e);throw new a("Cannot serialize values of type "+typeof e)}function u(e){if(e<-999999999999999||e>999999999999999)throw new a("Structured headers can only encode integers in the range range of -999,999,999,999,999 to 999,999,999,999,999 inclusive");return e.toString()}function l(e){const t=e.toFixed(3).replace(/0+$/,"");if(t.split(".")[0].replace("-","").length>12)throw new a("Fractional numbers are not allowed to have more than 12 significant digits before the decimal point");return t}function f(e){if(!(0,n.isAscii)(e))throw new a("Only ASCII strings may be serialized");return`"${e.replace(/("|\\)/g,(e=>"\\"+e))}"`}function d(e){let t='%"';const r=new TextEncoder;for(const i of r.encode(e.toString()))t+=37===i||34===i||i<=31||i>=127?"%"+i.toString(16):String.fromCharCode(i);return t+'"'}function w(e){return e?"?1":"?0"}function g(e){return`:${e.toBase64()}:`}function y(e){return e.toString()}function m(e){return"@"+Math.floor(e.getTime()/1e3)}function S(e){return Array.from(e).map((([e,t])=>{let r=";"+k(e);return!0!==t&&(r+="="+p(t)),r})).join("")}function k(e){if(!(0,n.isValidKeyStr)(e))throw new a("Keys in dictionaries must only contain lowercase letter, numbers, _-*. and must start with a letter or *");return e}t.SerializeError=a,t.serializeList=function(e){return e.map((e=>(0,n.isInnerList)(e)?c(e):h(e))).join(", ")},t.serializeDictionary=function(e){const t=e instanceof Map?e.entries():Object.entries(e);return Array.from(t).map((([e,t])=>{const r=k(e);return Array.isArray(t)?!0===t[0]?r+S(t[1]):(0,n.isInnerList)(t)?r+"="+c(t):r+"="+h(t):!0===t?r:r+"="+p(t)})).join(", ")},t.serializeItem=h,t.serializeInnerList=c,t.serializeBareItem=p,t.serializeInteger=u,t.serializeDecimal=l,t.serializeString=f,t.serializeDisplayString=d,t.serializeBoolean=w,t.serializeByteSequence=g,t.serializeToken=y,t.serializeDate=m,t.serializeParameters=S,t.serializeKey=k},0:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.Token=void 0;const i=r(128);t.Token=class{constructor(e){if(!(0,i.isValidTokenStr)(e))throw new TypeError("Invalid character in Token string. Tokens must start with *, A-Z and the rest of the string may only contain a-z, A-Z, 0-9, :/!#$%&'*+-.^_`|~");this.value=e}toString(){return this.value}}},985:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ByteSequence=void 0,t.ByteSequence=class{constructor(e){this.base64Value=e}toBase64(){return this.base64Value}}},128:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.isByteSequence=t.isInnerList=t.isValidKeyStr=t.isValidTokenStr=t.isAscii=void 0;const r=/^[\x20-\x7E]*$/,i=/^[a-zA-Z*][:/!#$%&'*+\-.^_`|~A-Za-z0-9]*$/,s=/^[a-z*][*\-_.a-z0-9]*$/;t.isAscii=function(e){return r.test(e)},t.isValidTokenStr=function(e){return i.test(e)},t.isValidKeyStr=function(e){return s.test(e)},t.isInnerList=function(e){return Array.isArray(e[0])},t.isByteSequence=function(e){return"object"==typeof e&&"base64Value"in e}}},t={},r=function r(i){var s=t[i];if(void 0!==s)return s.exports;var n=t[i]={exports:{}};return e[i].call(n.exports,n,n.exports,r),n.exports}(590);structuredHeader=r}(); | ||
//# sourceMappingURL=structured-header.min.js.map |
@@ -1,5 +0,6 @@ | ||
export * from './serializer'; | ||
export * from './parser'; | ||
export * from './types'; | ||
export * from './util'; | ||
export { Token } from './token'; | ||
export * from './serializer.js'; | ||
export * from './parser.js'; | ||
export * from './types.js'; | ||
export * from './util.js'; | ||
export { Token } from './token.js'; | ||
export { DisplayString } from './displaystring.js'; |
@@ -1,24 +0,7 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Token = void 0; | ||
__exportStar(require("./serializer"), exports); | ||
__exportStar(require("./parser"), exports); | ||
__exportStar(require("./types"), exports); | ||
__exportStar(require("./util"), exports); | ||
var token_1 = require("./token"); | ||
Object.defineProperty(exports, "Token", { enumerable: true, get: function () { return token_1.Token; } }); | ||
export * from './serializer.js'; | ||
export * from './parser.js'; | ||
export * from './types.js'; | ||
export * from './util.js'; | ||
export { Token } from './token.js'; | ||
export { DisplayString } from './displaystring.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { Dictionary, List, Item } from './types'; | ||
import { Dictionary, List, Item } from './types.js'; | ||
export declare function parseDictionary(input: string): Dictionary; | ||
@@ -21,5 +21,7 @@ export declare function parseList(input: string): List; | ||
private parseString; | ||
private parseDisplayString; | ||
private parseToken; | ||
private parseByteSequence; | ||
private parseBoolean; | ||
private parseDate; | ||
private parseKey; | ||
@@ -37,2 +39,3 @@ /** | ||
private getChar; | ||
private getChars; | ||
private eof; | ||
@@ -39,0 +42,0 @@ private skipOWS; |
@@ -1,23 +0,18 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ParseError = exports.parseItem = exports.parseList = exports.parseDictionary = void 0; | ||
const types_1 = require("./types"); | ||
const token_1 = require("./token"); | ||
const util_1 = require("./util"); | ||
function parseDictionary(input) { | ||
import { ByteSequence } from './types.js'; | ||
import { Token } from './token.js'; | ||
import { isAscii } from './util.js'; | ||
import { DisplayString } from './displaystring.js'; | ||
export function parseDictionary(input) { | ||
const parser = new Parser(input); | ||
return parser.parseDictionary(); | ||
} | ||
exports.parseDictionary = parseDictionary; | ||
function parseList(input) { | ||
export function parseList(input) { | ||
const parser = new Parser(input); | ||
return parser.parseList(); | ||
} | ||
exports.parseList = parseList; | ||
function parseItem(input) { | ||
export function parseItem(input) { | ||
const parser = new Parser(input); | ||
return parser.parseItem(); | ||
} | ||
exports.parseItem = parseItem; | ||
class ParseError extends Error { | ||
export class ParseError extends Error { | ||
constructor(position, message) { | ||
@@ -27,4 +22,3 @@ super(`Parse error: ${message} at offset ${position}`); | ||
} | ||
exports.ParseError = ParseError; | ||
class Parser { | ||
export default class Parser { | ||
constructor(input) { | ||
@@ -139,2 +133,8 @@ this.input = input; | ||
} | ||
if (char === '@') { | ||
return this.parseDate(); | ||
} | ||
if (char === '%') { | ||
return this.parseDisplayString(); | ||
} | ||
throw new ParseError(this.pos, 'Unexpected input'); | ||
@@ -232,3 +232,3 @@ } | ||
} | ||
else if (!(0, util_1.isAscii)(char)) { | ||
else if (!isAscii(char)) { | ||
throw new ParseError(this.pos, 'Strings must be in the ASCII range'); | ||
@@ -242,2 +242,38 @@ } | ||
} | ||
parseDisplayString() { | ||
const chars = this.getChars(2); | ||
if (chars !== '%"') { | ||
throw new ParseError(this.pos, 'Unexpected character. Display strings should start with %='); | ||
} | ||
const result = []; | ||
while (!this.eof()) { | ||
const char = this.getChar(); | ||
if (char.charCodeAt(0) <= 0x1F || (char.charCodeAt(0) >= 0x7F && char.charCodeAt(0) <= 0xFF)) { | ||
throw new ParseError(this.pos, 'Invalid char found in DisplayString. Did you forget to escape?'); | ||
} | ||
if (char === '%') { | ||
const hexChars = this.getChars(2); | ||
if (/^[0-9a-f]{2}$/.test(hexChars)) { | ||
result.push(parseInt(hexChars, 16)); | ||
} | ||
else { | ||
throw new ParseError(this.pos, `Unexpected sequence after % in DispalyString: "${hexChars}". Note that hexidecimals must be lowercase`); | ||
} | ||
continue; | ||
} | ||
if (char === '"') { | ||
const textDecoder = new TextDecoder('utf-8', { | ||
fatal: true | ||
}); | ||
try { | ||
return new DisplayString(textDecoder.decode(new Uint8Array(result))); | ||
} | ||
catch (err) { | ||
throw new ParseError(this.pos, 'Fatal error decoding UTF-8 sequence in Display String'); | ||
} | ||
} | ||
result.push(char.charCodeAt(0)); | ||
} | ||
throw new ParseError(this.pos, 'Unexpected end of input'); | ||
} | ||
parseToken() { | ||
@@ -252,7 +288,7 @@ // The specification wants this check, but it's an unreachable code block. | ||
if (char === undefined || !/^[:/!#$%&'*+\-.^_`|~A-Za-z0-9]$/.test(char)) { | ||
return new token_1.Token(outputString); | ||
return new Token(outputString); | ||
} | ||
outputString += this.getChar(); | ||
} | ||
return new token_1.Token(outputString); | ||
return new Token(outputString); | ||
} | ||
@@ -271,3 +307,3 @@ parseByteSequence() { | ||
} | ||
return new types_1.ByteSequence(b64Content); | ||
return new ByteSequence(b64Content); | ||
} | ||
@@ -286,5 +322,27 @@ parseBoolean() { | ||
} | ||
parseDate() { | ||
this.expectChar('@'); | ||
this.pos++; | ||
let sign = 1; | ||
let inputNumber = ''; | ||
if (this.lookChar() === '-') { | ||
sign = -1; | ||
this.pos++; | ||
} | ||
if (!isDigit(this.lookChar())) { | ||
throw new ParseError(this.pos, 'Expected a digit (0-9)'); | ||
} | ||
while (!this.eof()) { | ||
const char = this.getChar(); | ||
if (isDigit(char)) { | ||
inputNumber += char; | ||
} | ||
else { | ||
throw new ParseError(this.pos, 'Expected a digit (0-9), whitespace or EOL'); | ||
} | ||
} | ||
return new Date(parseInt(inputNumber, 10) * sign * 1000); | ||
} | ||
parseKey() { | ||
var _a; | ||
if (!((_a = this.lookChar()) === null || _a === void 0 ? void 0 : _a.match(/^[a-z*]/))) { | ||
if (!this.lookChar()?.match(/^[a-z*]/)) { | ||
throw new ParseError(this.pos, 'A key must begin with an asterisk or letter (a-z)'); | ||
@@ -321,2 +379,7 @@ } | ||
} | ||
getChars(count) { | ||
const result = this.input.substr(this.pos, count); | ||
this.pos += count; | ||
return result; | ||
} | ||
eof() { | ||
@@ -352,3 +415,2 @@ return this.pos >= this.input.length; | ||
} | ||
exports.default = Parser; | ||
const isDigitRegex = /^[0-9]$/; | ||
@@ -355,0 +417,0 @@ function isDigit(char) { |
@@ -1,8 +0,18 @@ | ||
import { BareItem, ByteSequence, Dictionary, InnerList, Item, List, Parameters } from './types'; | ||
import { Token } from './token'; | ||
import { BareItem, ByteSequence, Dictionary, DictionaryObject, InnerList, Item, List, Parameters } from './types.js'; | ||
import { Token } from './token.js'; | ||
import { DisplayString } from './displaystring.js'; | ||
export declare class SerializeError extends Error { | ||
} | ||
export declare function serializeList(input: List): string; | ||
export declare function serializeDictionary(input: Dictionary): string; | ||
export declare function serializeDictionary(input: Dictionary | DictionaryObject): string; | ||
/** | ||
* Serialize a Structured Fields Item. | ||
* | ||
* An Item is a standalone value like a string, number of date, followed by | ||
* an optional set of parameters. | ||
* | ||
* You can either pass the value in the first argument and parameters in the second, or pass both as a tuple. The later exists for symmetry with parseItem. | ||
*/ | ||
export declare function serializeItem(input: Item): string; | ||
export declare function serializeItem(input: BareItem, params?: Parameters): string; | ||
export declare function serializeInnerList(input: InnerList): string; | ||
@@ -13,6 +23,8 @@ export declare function serializeBareItem(input: BareItem): string; | ||
export declare function serializeString(input: string): string; | ||
export declare function serializeDisplayString(input: DisplayString): string; | ||
export declare function serializeBoolean(input: boolean): string; | ||
export declare function serializeByteSequence(input: ByteSequence): string; | ||
export declare function serializeToken(input: Token): string; | ||
export declare function serializeDate(input: Date): string; | ||
export declare function serializeParameters(input: Parameters): string; | ||
export declare function serializeKey(input: string): string; |
@@ -1,13 +0,10 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.serializeKey = exports.serializeParameters = exports.serializeToken = exports.serializeByteSequence = exports.serializeBoolean = exports.serializeString = exports.serializeDecimal = exports.serializeInteger = exports.serializeBareItem = exports.serializeInnerList = exports.serializeItem = exports.serializeDictionary = exports.serializeList = exports.SerializeError = void 0; | ||
const types_1 = require("./types"); | ||
const token_1 = require("./token"); | ||
const util_1 = require("./util"); | ||
class SerializeError extends Error { | ||
import { ByteSequence, } from './types.js'; | ||
import { Token } from './token.js'; | ||
import { isAscii, isInnerList, isValidKeyStr } from './util.js'; | ||
import { DisplayString } from './displaystring.js'; | ||
export class SerializeError extends Error { | ||
} | ||
exports.SerializeError = SerializeError; | ||
function serializeList(input) { | ||
export function serializeList(input) { | ||
return input.map(value => { | ||
if ((0, util_1.isInnerList)(value)) { | ||
if (isInnerList(value)) { | ||
return serializeInnerList(value); | ||
@@ -20,31 +17,41 @@ } | ||
} | ||
exports.serializeList = serializeList; | ||
function serializeDictionary(input) { | ||
return Array.from(input.entries()).map(([key, value]) => { | ||
let out = serializeKey(key); | ||
if (value[0] === true) { | ||
out += serializeParameters(value[1]); | ||
export function serializeDictionary(input) { | ||
const entries = input instanceof Map ? input.entries() : Object.entries(input); | ||
return Array.from(entries).map(([key, entry]) => { | ||
const keyStr = serializeKey(key); | ||
if (Array.isArray(entry)) { | ||
if (entry[0] === true) { | ||
return keyStr + serializeParameters(entry[1]); | ||
} | ||
else { | ||
if (isInnerList(entry)) { | ||
return keyStr + '=' + serializeInnerList(entry); | ||
} | ||
else { | ||
return keyStr + '=' + serializeItem(entry); | ||
} | ||
} | ||
} | ||
else { | ||
out += '='; | ||
if ((0, util_1.isInnerList)(value)) { | ||
out += serializeInnerList(value); | ||
if (entry === true) { | ||
return keyStr; | ||
} | ||
else { | ||
out += serializeItem(value); | ||
return keyStr + '=' + serializeBareItem(entry); | ||
} | ||
} | ||
return out; | ||
}).join(', '); | ||
} | ||
exports.serializeDictionary = serializeDictionary; | ||
function serializeItem(input) { | ||
return serializeBareItem(input[0]) + serializeParameters(input[1]); | ||
export function serializeItem(input, params) { | ||
if (Array.isArray(input)) { | ||
return serializeBareItem(input[0]) + serializeParameters(input[1]); | ||
} | ||
else { | ||
return serializeBareItem(input) + (params ? serializeParameters(params) : ''); | ||
} | ||
} | ||
exports.serializeItem = serializeItem; | ||
function serializeInnerList(input) { | ||
export function serializeInnerList(input) { | ||
return `(${input[0].map(value => serializeItem(value)).join(' ')})${serializeParameters(input[1])}`; | ||
} | ||
exports.serializeInnerList = serializeInnerList; | ||
function serializeBareItem(input) { | ||
export function serializeBareItem(input) { | ||
if (typeof input === 'number') { | ||
@@ -59,8 +66,14 @@ if (Number.isInteger(input)) { | ||
} | ||
if (input instanceof token_1.Token) { | ||
if (input instanceof Token) { | ||
return serializeToken(input); | ||
} | ||
if (input instanceof types_1.ByteSequence) { | ||
if (input instanceof ByteSequence) { | ||
return serializeByteSequence(input); | ||
} | ||
if (input instanceof DisplayString) { | ||
return serializeDisplayString(input); | ||
} | ||
if (input instanceof Date) { | ||
return serializeDate(input); | ||
} | ||
if (typeof input === 'boolean') { | ||
@@ -71,4 +84,3 @@ return serializeBoolean(input); | ||
} | ||
exports.serializeBareItem = serializeBareItem; | ||
function serializeInteger(input) { | ||
export function serializeInteger(input) { | ||
if (input < -999999999999999 || input > 999999999999999) { | ||
@@ -79,4 +91,3 @@ throw new SerializeError('Structured headers can only encode integers in the range range of -999,999,999,999,999 to 999,999,999,999,999 inclusive'); | ||
} | ||
exports.serializeInteger = serializeInteger; | ||
function serializeDecimal(input) { | ||
export function serializeDecimal(input) { | ||
const out = input.toFixed(3).replace(/0+$/, ''); | ||
@@ -89,5 +100,4 @@ const signifantDigits = out.split('.')[0].replace('-', '').length; | ||
} | ||
exports.serializeDecimal = serializeDecimal; | ||
function serializeString(input) { | ||
if (!(0, util_1.isAscii)(input)) { | ||
export function serializeString(input) { | ||
if (!isAscii(input)) { | ||
throw new SerializeError('Only ASCII strings may be serialized'); | ||
@@ -97,16 +107,31 @@ } | ||
} | ||
exports.serializeString = serializeString; | ||
function serializeBoolean(input) { | ||
export function serializeDisplayString(input) { | ||
let out = '%"'; | ||
const textEncoder = new TextEncoder(); | ||
for (const char of textEncoder.encode(input.toString())) { | ||
if (char === 0x25 // % | ||
|| char === 0x22 // " | ||
|| char <= 0x1f | ||
|| char >= 0x7f) { | ||
out += '%' + char.toString(16); | ||
} | ||
else { | ||
out += String.fromCharCode(char); | ||
} | ||
} | ||
return out + '"'; | ||
} | ||
export function serializeBoolean(input) { | ||
return input ? '?1' : '?0'; | ||
} | ||
exports.serializeBoolean = serializeBoolean; | ||
function serializeByteSequence(input) { | ||
export function serializeByteSequence(input) { | ||
return `:${input.toBase64()}:`; | ||
} | ||
exports.serializeByteSequence = serializeByteSequence; | ||
function serializeToken(input) { | ||
export function serializeToken(input) { | ||
return input.toString(); | ||
} | ||
exports.serializeToken = serializeToken; | ||
function serializeParameters(input) { | ||
export function serializeDate(input) { | ||
return '@' + Math.floor(input.getTime() / 1000); | ||
} | ||
export function serializeParameters(input) { | ||
return Array.from(input).map(([key, value]) => { | ||
@@ -120,5 +145,4 @@ let out = ';' + serializeKey(key); | ||
} | ||
exports.serializeParameters = serializeParameters; | ||
function serializeKey(input) { | ||
if (!(0, util_1.isValidKeyStr)(input)) { | ||
export function serializeKey(input) { | ||
if (!isValidKeyStr(input)) { | ||
throw new SerializeError('Keys in dictionaries must only contain lowercase letter, numbers, _-*. and must start with a letter or *'); | ||
@@ -128,3 +152,2 @@ } | ||
} | ||
exports.serializeKey = serializeKey; | ||
//# sourceMappingURL=serializer.js.map |
@@ -1,8 +0,5 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Token = void 0; | ||
const util_1 = require("./util"); | ||
class Token { | ||
import { isValidTokenStr } from './util.js'; | ||
export class Token { | ||
constructor(value) { | ||
if (!(0, util_1.isValidTokenStr)(value)) { | ||
if (!isValidTokenStr(value)) { | ||
throw new TypeError('Invalid character in Token string. Tokens must start with *, A-Z and the rest of the string may only contain a-z, A-Z, 0-9, :/!#$%&\'*+-.^_`|~'); | ||
@@ -16,3 +13,2 @@ } | ||
} | ||
exports.Token = Token; | ||
//# sourceMappingURL=token.js.map |
import { Token } from './token'; | ||
import { DisplayString } from './displaystring'; | ||
/** | ||
@@ -28,2 +29,9 @@ * Lists are arrays of zero or more members, each of which can be an Item | ||
export type Dictionary = Map<string, Item | InnerList>; | ||
/** | ||
* Another representatation of a Dictionary. | ||
* | ||
* Serialize functions also accept an Object instead of a Map for a | ||
* Dictionary. Parse functions will always return the Map however. | ||
*/ | ||
export type DictionaryObject = Record<string, BareItem | Item | InnerList>; | ||
export declare class ByteSequence { | ||
@@ -34,3 +42,3 @@ base64Value: string; | ||
} | ||
export type BareItem = number | string | Token | ByteSequence | boolean; | ||
export type BareItem = number | string | Token | ByteSequence | Date | boolean | DisplayString; | ||
export type Item = [BareItem, Parameters]; |
@@ -1,5 +0,2 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ByteSequence = void 0; | ||
class ByteSequence { | ||
export class ByteSequence { | ||
constructor(base64Value) { | ||
@@ -12,3 +9,2 @@ this.base64Value = base64Value; | ||
} | ||
exports.ByteSequence = ByteSequence; | ||
//# sourceMappingURL=types.js.map |
@@ -1,27 +0,19 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isByteSequence = exports.isInnerList = exports.isValidKeyStr = exports.isValidTokenStr = exports.isAscii = void 0; | ||
const asciiRe = /^[\x20-\x7E]*$/; | ||
const tokenRe = /^[a-zA-Z*][:/!#$%&'*+\-.^_`|~A-Za-z0-9]*$/; | ||
const keyRe = /^[a-z*][*\-_.a-z0-9]*$/; | ||
function isAscii(str) { | ||
export function isAscii(str) { | ||
return asciiRe.test(str); | ||
} | ||
exports.isAscii = isAscii; | ||
function isValidTokenStr(str) { | ||
export function isValidTokenStr(str) { | ||
return tokenRe.test(str); | ||
} | ||
exports.isValidTokenStr = isValidTokenStr; | ||
function isValidKeyStr(str) { | ||
export function isValidKeyStr(str) { | ||
return keyRe.test(str); | ||
} | ||
exports.isValidKeyStr = isValidKeyStr; | ||
function isInnerList(input) { | ||
export function isInnerList(input) { | ||
return Array.isArray(input[0]); | ||
} | ||
exports.isInnerList = isInnerList; | ||
function isByteSequence(input) { | ||
export function isByteSequence(input) { | ||
return typeof input === 'object' && 'base64Value' in input; | ||
} | ||
exports.isByteSequence = isByteSequence; | ||
//# sourceMappingURL=util.js.map |
{ | ||
"name": "structured-headers", | ||
"version": "1.0.1", | ||
"version": "2.0.0-alpha.0", | ||
"description": "Implementation of RFC8941, structured headers for HTTP.", | ||
"main": "dist/index.js", | ||
"type": "module", | ||
"exports": "dist/index.js", | ||
"source": "src/index.ts", | ||
"types": "dist/index.d.ts", | ||
"scripts": { | ||
@@ -38,43 +41,15 @@ "test": "make test", | ||
"@types/chai": "^4.3.3", | ||
"@types/mocha": "^10.0.1", | ||
"@types/node": "^12.20.13", | ||
"@typescript-eslint/eslint-plugin": "^5.62.0", | ||
"@typescript-eslint/parser": "^5.62.0", | ||
"@types/node": "^18.19.10", | ||
"@typescript-eslint/eslint-plugin": "^6.19.1", | ||
"@typescript-eslint/parser": "^6.19.1", | ||
"base32-decode": "^1.0.0", | ||
"base32-encode": "^1.1.1", | ||
"chai": "^4.2.0", | ||
"base32-encode": "^2.0.0", | ||
"chai": "^5.0.3", | ||
"eslint": "^8.23.0", | ||
"mocha": "^10.0.0", | ||
"nyc": "^15.1.0", | ||
"ts-node": "^10.0.0", | ||
"typescript": "^5.1.3", | ||
"webpack": "^5.86.0", | ||
"webpack-cli": "^5.1.4" | ||
"typescript": "^5.1.3" | ||
}, | ||
"nyc": { | ||
"extension": [ | ||
".ts" | ||
] | ||
}, | ||
"mocha": { | ||
"require": [ | ||
"ts-node/register" | ||
], | ||
"recursive": true, | ||
"extension": [ | ||
"ts", | ||
"js", | ||
"tsx" | ||
], | ||
"exit": true | ||
}, | ||
"browserslist": [ | ||
"last 2 versions", | ||
"not ie 11", | ||
"not op_mini all" | ||
], | ||
"engines": { | ||
"npm": ">=6", | ||
"node": ">= 14" | ||
"node": ">=18" | ||
} | ||
} |
@@ -17,3 +17,3 @@ Structured Headers parser for Javascript | ||
This package has 2725 unittests, the vast majority are supplied from the | ||
This package has 2740 unittests, the vast majority are supplied from the | ||
official [HTTP WG test suite][2]. | ||
@@ -63,3 +63,3 @@ | ||
``` | ||
# Parsed as string | ||
# Parsed an ASCII string | ||
Header: "foo" | ||
@@ -70,2 +70,7 @@ | ||
# A Unicode string, called a 'Display String' in the spec. They use | ||
# percent encoding, but encode a different set of characters than | ||
# URLs. | ||
Header %"Frysl%C3%A2n" | ||
# Parsed as number | ||
@@ -85,5 +90,7 @@ Header: 5 | ||
Header: "Hello world"; a="5" | ||
# Parsed into a Date object | ||
Header: @1686634251 | ||
``` | ||
To parse these header values, use the `parseItem`: | ||
@@ -105,5 +112,4 @@ | ||
```typescript | ||
// The raw value | ||
type BareItem = number | string | Token | ByteSequence | boolean; | ||
type BareItem = number | string | Token | ByteSequence | boolean | Date | DisplayString; | ||
@@ -200,3 +206,2 @@ // The return type of parseItem | ||
// Returns "foo", "bar" | ||
@@ -209,21 +214,34 @@ serializeList([ | ||
// Returns a=1, b=?0 | ||
sh.serializeDictionary(new Map([ | ||
['a', [1, new Map()]], | ||
['b', [false, new Map()]], | ||
])); | ||
sh.serializeDictionary({ | ||
a: 1, | ||
b: false, | ||
}); | ||
// Returns 42 | ||
serializeItem([42, new Map()]); | ||
serializeItem(42); | ||
// Returns 5.5 | ||
serializeItem([5.5, new Map()]); | ||
serializeItem(5.5); | ||
// Returns "hello world" | ||
serializeItem(["hello world", new Map()]); | ||
serializeItem("hello world"); | ||
// Returns %"Frysl%C3%A2n" | ||
serializeItem("Fryslân"); | ||
// Returns ?1 | ||
serializeItem([true, new Map()]); | ||
serializeItem(true); | ||
// Returns a base-64 representation like: *aGVsbG8=* | ||
serializeItem([new ByteSequence('aGVsbG8='), new Map()]); | ||
serializeItem(new ByteSequence('aGVsbG8=')); | ||
// Returns a unix timestamp | ||
serializeItem(new Date()); | ||
// Parameters to items can be passed as the second argument | ||
// Returns "hello", q=5 | ||
serializeItem( | ||
"hello", | ||
new Map(['q', 5]) | ||
); | ||
``` | ||
@@ -230,0 +248,0 @@ |
@@ -1,5 +0,6 @@ | ||
export * from './serializer'; | ||
export * from './parser'; | ||
export * from './types'; | ||
export * from './util'; | ||
export { Token } from './token'; | ||
export * from './serializer.js'; | ||
export * from './parser.js'; | ||
export * from './types.js'; | ||
export * from './util.js'; | ||
export { Token } from './token.js'; | ||
export { DisplayString } from './displaystring.js'; |
@@ -9,7 +9,8 @@ import { | ||
ByteSequence | ||
} from './types'; | ||
} from './types.js'; | ||
import { Token } from './token'; | ||
import { Token } from './token.js'; | ||
import { isAscii } from './util'; | ||
import { isAscii } from './util.js'; | ||
import { DisplayString } from './displaystring.js'; | ||
@@ -186,3 +187,8 @@ export function parseDictionary(input: string): Dictionary { | ||
} | ||
if (char === '@') { | ||
return this.parseDate(); | ||
} | ||
if (char === '%') { | ||
return this.parseDisplayString(); | ||
} | ||
throw new ParseError(this.pos, 'Unexpected input'); | ||
@@ -302,2 +308,45 @@ | ||
private parseDisplayString(): DisplayString { | ||
const chars = this.getChars(2); | ||
if (chars !== '%"') { | ||
throw new ParseError(this.pos, 'Unexpected character. Display strings should start with %='); | ||
} | ||
const result:number[] = []; | ||
while (!this.eof()) { | ||
const char = this.getChar(); | ||
if (char.charCodeAt(0) <= 0x1F || (char.charCodeAt(0) >= 0x7F && char.charCodeAt(0) <= 0xFF)) { | ||
throw new ParseError(this.pos, 'Invalid char found in DisplayString. Did you forget to escape?'); | ||
} | ||
if (char==='%') { | ||
const hexChars = this.getChars(2); | ||
if (/^[0-9a-f]{2}$/.test(hexChars)) { | ||
result.push(parseInt(hexChars, 16)); | ||
} else { | ||
throw new ParseError(this.pos, `Unexpected sequence after % in DispalyString: "${hexChars}". Note that hexidecimals must be lowercase`); | ||
} | ||
continue; | ||
} | ||
if (char==='"') { | ||
const textDecoder = new TextDecoder('utf-8', { | ||
fatal: true | ||
}); | ||
try { | ||
return new DisplayString( | ||
textDecoder.decode(new Uint8Array(result)) | ||
); | ||
} catch (err) { | ||
throw new ParseError(this.pos, 'Fatal error decoding UTF-8 sequence in Display String'); | ||
} | ||
} | ||
result.push(char.charCodeAt(0)); | ||
} | ||
throw new ParseError(this.pos, 'Unexpected end of input'); | ||
} | ||
private parseToken(): Token { | ||
@@ -359,2 +408,30 @@ | ||
private parseDate(): Date { | ||
this.expectChar('@'); | ||
this.pos++; | ||
let sign = 1; | ||
let inputNumber = ''; | ||
if (this.lookChar()==='-') { | ||
sign = -1; | ||
this.pos++; | ||
} | ||
if (!isDigit(this.lookChar())) { | ||
throw new ParseError(this.pos, 'Expected a digit (0-9)'); | ||
} | ||
while(!this.eof()) { | ||
const char = this.getChar(); | ||
if (isDigit(char)) { | ||
inputNumber+=char; | ||
} else { | ||
throw new ParseError(this.pos, 'Expected a digit (0-9), whitespace or EOL'); | ||
} | ||
} | ||
return new Date(parseInt(inputNumber,10)*sign*1000); | ||
} | ||
private parseKey(): string { | ||
@@ -407,2 +484,12 @@ | ||
} | ||
private getChars(count: number): string { | ||
const result = this.input.substr( | ||
this.pos, | ||
count | ||
); | ||
this.pos += count; | ||
return result; | ||
} | ||
private eof():boolean { | ||
@@ -409,0 +496,0 @@ |
@@ -5,2 +5,3 @@ import { | ||
Dictionary, | ||
DictionaryObject, | ||
InnerList, | ||
@@ -10,7 +11,8 @@ Item, | ||
Parameters, | ||
} from './types'; | ||
} from './types.js'; | ||
import { Token } from './token'; | ||
import { Token } from './token.js'; | ||
import { isAscii, isInnerList, isValidKeyStr } from './util'; | ||
import { isAscii, isInnerList, isValidKeyStr } from './util.js'; | ||
import { DisplayString } from './displaystring.js'; | ||
@@ -33,20 +35,27 @@ export class SerializeError extends Error {} | ||
export function serializeDictionary(input: Dictionary): string { | ||
export function serializeDictionary(input: Dictionary | DictionaryObject): string { | ||
return Array.from( | ||
input.entries() | ||
).map(([key, value]) => { | ||
const entries: Iterable<[string, BareItem|Item|InnerList]> = input instanceof Map ? input.entries() : Object.entries(input); | ||
let out = serializeKey(key); | ||
if (value[0]===true) { | ||
out += serializeParameters(value[1]); | ||
return Array.from(entries).map(([key, entry]) => { | ||
const keyStr = serializeKey(key); | ||
if (Array.isArray(entry)) { | ||
if (entry[0]===true) { | ||
return keyStr + serializeParameters(entry[1]); | ||
} else { | ||
if (isInnerList(entry)) { | ||
return keyStr + '=' + serializeInnerList(entry); | ||
} else { | ||
return keyStr + '=' + serializeItem(entry); | ||
} | ||
} | ||
} else { | ||
out += '='; | ||
if (isInnerList(value)) { | ||
out += serializeInnerList(value); | ||
if (entry===true) { | ||
return keyStr; | ||
} else { | ||
out += serializeItem(value); | ||
return keyStr + '=' + serializeBareItem(entry); | ||
} | ||
} | ||
return out; | ||
@@ -57,5 +66,19 @@ }).join(', '); | ||
export function serializeItem(input: Item): string { | ||
/** | ||
* Serialize a Structured Fields Item. | ||
* | ||
* An Item is a standalone value like a string, number of date, followed by | ||
* an optional set of parameters. | ||
* | ||
* You can either pass the value in the first argument and parameters in the second, or pass both as a tuple. The later exists for symmetry with parseItem. | ||
*/ | ||
export function serializeItem(input: Item): string; | ||
export function serializeItem(input: BareItem, params?: Parameters): string; | ||
export function serializeItem(input: Item|BareItem, params?: Parameters): string { | ||
return serializeBareItem(input[0]) + serializeParameters(input[1]); | ||
if (Array.isArray(input)) { | ||
return serializeBareItem(input[0]) + serializeParameters(input[1]); | ||
} else { | ||
return serializeBareItem(input) + (params?serializeParameters(params):''); | ||
} | ||
@@ -87,2 +110,8 @@ } | ||
} | ||
if (input instanceof DisplayString) { | ||
return serializeDisplayString(input); | ||
} | ||
if (input instanceof Date) { | ||
return serializeDate(input); | ||
} | ||
if (typeof input === 'boolean') { | ||
@@ -119,2 +148,20 @@ return serializeBoolean(input); | ||
export function serializeDisplayString(input: DisplayString): string { | ||
let out = '%"'; | ||
const textEncoder = new TextEncoder(); | ||
for (const char of textEncoder.encode(input.toString())) { | ||
if ( | ||
char === 0x25 // % | ||
|| char === 0x22 // " | ||
|| char <= 0x1f | ||
|| char >= 0x7f | ||
) { | ||
out += '%' + char.toString(16); | ||
} else { | ||
out += String.fromCharCode(char); | ||
} | ||
} | ||
return out + '"'; | ||
} | ||
export function serializeBoolean(input: boolean): string { | ||
@@ -132,2 +179,6 @@ return input ? '?1' : '?0'; | ||
export function serializeDate(input: Date): string { | ||
return '@' + Math.floor(input.getTime()/1000); | ||
} | ||
export function serializeParameters(input: Parameters): string { | ||
@@ -134,0 +185,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { isValidTokenStr } from './util'; | ||
import { isValidTokenStr } from './util.js'; | ||
@@ -3,0 +3,0 @@ export class Token { |
import { Token } from './token'; | ||
import { DisplayString } from './displaystring'; | ||
@@ -33,2 +34,10 @@ /** | ||
/** | ||
* Another representatation of a Dictionary. | ||
* | ||
* Serialize functions also accept an Object instead of a Map for a | ||
* Dictionary. Parse functions will always return the Map however. | ||
*/ | ||
export type DictionaryObject = Record<string, BareItem|Item|InnerList>; | ||
export class ByteSequence { | ||
@@ -51,4 +60,4 @@ | ||
export type BareItem = number | string | Token | ByteSequence | boolean; | ||
export type BareItem = number | string | Token | ByteSequence | Date | boolean | DisplayString; | ||
export type Item = [BareItem, Parameters]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
124355
9
34
1451
252
Yes
1