Comparing version 0.1.3 to 0.3.0
@@ -18,4 +18,2 @@ interface SqidsOptions { | ||
decode(id: string): number[]; | ||
minValue(): number; | ||
maxValue(): number; | ||
private encodeNumbers; | ||
@@ -26,3 +24,4 @@ private shuffle; | ||
private isBlockedId; | ||
private maxValue; | ||
} | ||
export {}; |
@@ -575,6 +575,9 @@ "use strict"; | ||
const minLength = (_b = options === null || options === void 0 ? void 0 : options.minLength) !== null && _b !== void 0 ? _b : exports.defaultOptions.minLength; | ||
const blocklist = (_c = options === null || options === void 0 ? void 0 : options.blocklist) !== null && _c !== void 0 ? _c : new Set(exports.defaultOptions.blocklist); | ||
const minAlphabetLength = 5; | ||
const blocklist = (_c = options === null || options === void 0 ? void 0 : options.blocklist) !== null && _c !== void 0 ? _c : exports.defaultOptions.blocklist; | ||
if (new Blob([alphabet]).size !== alphabet.length) { | ||
throw new Error('Alphabet cannot contain multibyte characters'); | ||
} | ||
const minAlphabetLength = 3; | ||
if (alphabet.length < minAlphabetLength) { | ||
throw new Error('Alphabet length must be at least 5'); | ||
throw new Error(`Alphabet length must be at least ${minAlphabetLength}`); | ||
} | ||
@@ -584,6 +587,7 @@ if (new Set(alphabet).size !== alphabet.length) { | ||
} | ||
const minLengthLimit = 255; | ||
if (typeof minLength !== 'number' || | ||
minLength < this.minValue() || | ||
minLength > alphabet.length) { | ||
throw new TypeError(`Minimum length has to be between ${this.minValue()} and ${alphabet.length}`); | ||
minLength < 0 || | ||
minLength > minLengthLimit) { | ||
throw new Error(`Minimum length has to be between 0 and ${minLengthLimit}`); | ||
} | ||
@@ -610,7 +614,7 @@ const filteredBlocklist = new Set(); | ||
} | ||
const inRangeNumbers = numbers.filter((n) => n >= this.minValue() && n <= this.maxValue()); | ||
const inRangeNumbers = numbers.filter((n) => n >= 0 && n <= this.maxValue()); | ||
if (inRangeNumbers.length !== numbers.length) { | ||
throw new Error(`Encoding supports numbers between ${this.minValue()} and ${this.maxValue()}`); | ||
throw new Error(`Encoding supports numbers between 0 and ${this.maxValue()}`); | ||
} | ||
return this.encodeNumbers(numbers, false); | ||
return this.encodeNumbers(numbers); | ||
} | ||
@@ -631,21 +635,12 @@ decode(id) { | ||
let alphabet = this.alphabet.slice(offset) + this.alphabet.slice(0, offset); | ||
const partition = alphabet.charAt(1); | ||
alphabet = alphabet.slice(2); | ||
alphabet = alphabet.split('').reverse().join(''); | ||
let slicedId = id.slice(1); | ||
const partitionIndex = slicedId.indexOf(partition); | ||
if (partitionIndex > 0 && partitionIndex < slicedId.length - 1) { | ||
slicedId = slicedId.slice(partitionIndex + 1); | ||
alphabet = this.shuffle(alphabet); | ||
} | ||
while (slicedId.length > 0) { | ||
const separator = alphabet.slice(-1); | ||
const separator = alphabet.slice(0, 1); | ||
const chunks = slicedId.split(separator); | ||
if (chunks.length > 0) { | ||
const alphabetWithoutSeparator = alphabet.slice(0, -1); | ||
for (const c of chunks[0]) { | ||
if (!alphabetWithoutSeparator.includes(c)) { | ||
return []; | ||
} | ||
if (chunks[0] === '') { | ||
return ret; | ||
} | ||
ret.push(this.toNumber(chunks[0], alphabetWithoutSeparator)); | ||
ret.push(this.toNumber(chunks[0], alphabet.slice(1))); | ||
if (chunks.length > 1) { | ||
@@ -659,27 +654,17 @@ alphabet = this.shuffle(alphabet); | ||
} | ||
minValue() { | ||
return 0; | ||
} | ||
maxValue() { | ||
return Number.MAX_SAFE_INTEGER; | ||
} | ||
encodeNumbers(numbers, partitioned = false) { | ||
const offset = numbers.reduce((a, v, i) => this.alphabet[v % this.alphabet.length].codePointAt(0) + i + a, numbers.length) % this.alphabet.length; | ||
encodeNumbers(numbers, increment = 0) { | ||
if (increment > this.alphabet.length) { | ||
throw new Error('Reached max attempts to re-generate the ID'); | ||
} | ||
let offset = numbers.reduce((a, v, i) => this.alphabet[v % this.alphabet.length].codePointAt(0) + i + a, numbers.length) % this.alphabet.length; | ||
offset = (offset + increment) % this.alphabet.length; | ||
let alphabet = this.alphabet.slice(offset) + this.alphabet.slice(0, offset); | ||
const prefix = alphabet.charAt(0); | ||
const partition = alphabet.charAt(1); | ||
alphabet = alphabet.slice(2); | ||
alphabet = alphabet.split('').reverse().join(''); | ||
const ret = [prefix]; | ||
for (let i = 0; i !== numbers.length; i++) { | ||
const num = numbers[i]; | ||
const alphabetWithoutSeparator = alphabet.slice(0, -1); | ||
ret.push(this.toId(num, alphabetWithoutSeparator)); | ||
ret.push(this.toId(num, alphabet.slice(1))); | ||
if (i < numbers.length - 1) { | ||
const separator = alphabet.slice(-1); | ||
if (partitioned && i === 0) { | ||
ret.push(partition); | ||
} | ||
else { | ||
ret.push(separator); | ||
} | ||
ret.push(alphabet.slice(0, 1)); | ||
alphabet = this.shuffle(alphabet); | ||
@@ -690,27 +675,10 @@ } | ||
if (this.minLength > id.length) { | ||
if (!partitioned) { | ||
const newNumbers = [0, ...numbers]; | ||
id = this.encodeNumbers(newNumbers, true); | ||
id += alphabet.slice(0, 1); | ||
while (this.minLength - id.length > 0) { | ||
alphabet = this.shuffle(alphabet); | ||
id += alphabet.slice(0, Math.min(this.minLength - id.length, alphabet.length)); | ||
} | ||
if (this.minLength > id.length) { | ||
id = | ||
id.slice(0, 1) + | ||
alphabet.slice(0, this.minLength - id.length) + | ||
id.slice(1); | ||
} | ||
} | ||
if (this.isBlockedId(id)) { | ||
let newNumbers = numbers; | ||
if (partitioned) { | ||
if (numbers[0] + 1 > this.maxValue()) { | ||
throw new Error('Ran out of range checking against the blocklist'); | ||
} | ||
else { | ||
newNumbers[0] += 1; | ||
} | ||
} | ||
else { | ||
newNumbers = [0, ...numbers]; | ||
} | ||
id = this.encodeNumbers(newNumbers, true); | ||
id = this.encodeNumbers(numbers, increment + 1); | ||
} | ||
@@ -763,4 +731,7 @@ return id; | ||
} | ||
maxValue() { | ||
return Number.MAX_SAFE_INTEGER; | ||
} | ||
} | ||
exports.default = Sqids; | ||
//# sourceMappingURL=sqids.js.map |
@@ -1,2 +0,2 @@ | ||
!function(e,a){"object"==typeof exports&&"object"==typeof module?module.exports=a():"function"==typeof define&&define.amd?define("Sqids",[],a):"object"==typeof exports?exports.Sqids=a():e.Sqids=a()}(self,(()=>(()=>{"use strict";var e={d:(a,t)=>{for(var c in t)e.o(t,c)&&!e.o(a,c)&&Object.defineProperty(a,c,{enumerable:!0,get:t[c]})},o:(e,a)=>Object.prototype.hasOwnProperty.call(e,a)},a={};e.d(a,{default:()=>c});const t={alphabet:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",minLength:0,blocklist:new Set(["0rgasm","1d10t","1d1ot","1di0t","1diot","1eccacu10","1eccacu1o","1eccacul0","1eccaculo","1mbec11e","1mbec1le","1mbeci1e","1mbecile","a11upat0","a11upato","a1lupat0","a1lupato","aand","ah01e","ah0le","aho1e","ahole","al1upat0","al1upato","allupat0","allupato","ana1","ana1e","anal","anale","anus","arrapat0","arrapato","arsch","arse","ass","b00b","b00be","b01ata","b0ceta","b0iata","b0ob","b0obe","b0sta","b1tch","b1te","b1tte","ba1atkar","balatkar","bastard0","bastardo","batt0na","battona","bitch","bite","bitte","bo0b","bo0be","bo1ata","boceta","boiata","boob","boobe","bosta","bran1age","bran1er","bran1ette","bran1eur","bran1euse","branlage","branler","branlette","branleur","branleuse","c0ck","c0g110ne","c0g11one","c0g1i0ne","c0g1ione","c0gl10ne","c0gl1one","c0gli0ne","c0glione","c0na","c0nnard","c0nnasse","c0nne","c0u111es","c0u11les","c0u1l1es","c0u1lles","c0ui11es","c0ui1les","c0uil1es","c0uilles","c11t","c11t0","c11to","c1it","c1it0","c1ito","cabr0n","cabra0","cabrao","cabron","caca","cacca","cacete","cagante","cagar","cagare","cagna","cara1h0","cara1ho","caracu10","caracu1o","caracul0","caraculo","caralh0","caralho","cazz0","cazz1mma","cazzata","cazzimma","cazzo","ch00t1a","ch00t1ya","ch00tia","ch00tiya","ch0d","ch0ot1a","ch0ot1ya","ch0otia","ch0otiya","ch1asse","ch1avata","ch1er","ch1ng0","ch1ngadaz0s","ch1ngadazos","ch1ngader1ta","ch1ngaderita","ch1ngar","ch1ngo","ch1ngues","ch1nk","chatte","chiasse","chiavata","chier","ching0","chingadaz0s","chingadazos","chingader1ta","chingaderita","chingar","chingo","chingues","chink","cho0t1a","cho0t1ya","cho0tia","cho0tiya","chod","choot1a","choot1ya","chootia","chootiya","cl1t","cl1t0","cl1to","clit","clit0","clito","cock","cog110ne","cog11one","cog1i0ne","cog1ione","cogl10ne","cogl1one","cogli0ne","coglione","cona","connard","connasse","conne","cou111es","cou11les","cou1l1es","cou1lles","coui11es","coui1les","couil1es","couilles","cracker","crap","cu10","cu1att0ne","cu1attone","cu1er0","cu1ero","cu1o","cul0","culatt0ne","culattone","culer0","culero","culo","cum","cunt","d11d0","d11do","d1ck","d1ld0","d1ldo","damn","de1ch","deich","depp","di1d0","di1do","dick","dild0","dildo","dyke","encu1e","encule","enema","enf01re","enf0ire","enfo1re","enfoire","estup1d0","estup1do","estupid0","estupido","etr0n","etron","f0da","f0der","f0ttere","f0tters1","f0ttersi","f0tze","f0utre","f1ca","f1cker","f1ga","fag","fica","ficker","figa","foda","foder","fottere","fotters1","fottersi","fotze","foutre","fr0c10","fr0c1o","fr0ci0","fr0cio","fr0sc10","fr0sc1o","fr0sci0","fr0scio","froc10","froc1o","froci0","frocio","frosc10","frosc1o","frosci0","froscio","fuck","g00","g0o","g0u1ne","g0uine","gandu","go0","goo","gou1ne","gouine","gr0gnasse","grognasse","haram1","harami","haramzade","hund1n","hundin","id10t","id1ot","idi0t","idiot","imbec11e","imbec1le","imbeci1e","imbecile","j1zz","jerk","jizz","k1ke","kam1ne","kamine","kike","leccacu10","leccacu1o","leccacul0","leccaculo","m1erda","m1gn0tta","m1gnotta","m1nch1a","m1nchia","m1st","mam0n","mamahuev0","mamahuevo","mamon","masturbat10n","masturbat1on","masturbate","masturbati0n","masturbation","merd0s0","merd0so","merda","merde","merdos0","merdoso","mierda","mign0tta","mignotta","minch1a","minchia","mist","musch1","muschi","n1gger","neger","negr0","negre","negro","nerch1a","nerchia","nigger","orgasm","p00p","p011a","p01la","p0l1a","p0lla","p0mp1n0","p0mp1no","p0mpin0","p0mpino","p0op","p0rca","p0rn","p0rra","p0uff1asse","p0uffiasse","p1p1","p1pi","p1r1a","p1rla","p1sc10","p1sc1o","p1sci0","p1scio","p1sser","pa11e","pa1le","pal1e","palle","pane1e1r0","pane1e1ro","pane1eir0","pane1eiro","panele1r0","panele1ro","paneleir0","paneleiro","patakha","pec0r1na","pec0rina","pecor1na","pecorina","pen1s","pendej0","pendejo","penis","pip1","pipi","pir1a","pirla","pisc10","pisc1o","pisci0","piscio","pisser","po0p","po11a","po1la","pol1a","polla","pomp1n0","pomp1no","pompin0","pompino","poop","porca","porn","porra","pouff1asse","pouffiasse","pr1ck","prick","pussy","put1za","puta","puta1n","putain","pute","putiza","puttana","queca","r0mp1ba11e","r0mp1ba1le","r0mp1bal1e","r0mp1balle","r0mpiba11e","r0mpiba1le","r0mpibal1e","r0mpiballe","rand1","randi","rape","recch10ne","recch1one","recchi0ne","recchione","retard","romp1ba11e","romp1ba1le","romp1bal1e","romp1balle","rompiba11e","rompiba1le","rompibal1e","rompiballe","ruff1an0","ruff1ano","ruffian0","ruffiano","s1ut","sa10pe","sa1aud","sa1ope","sacanagem","sal0pe","salaud","salope","saugnapf","sb0rr0ne","sb0rra","sb0rrone","sbattere","sbatters1","sbattersi","sborr0ne","sborra","sborrone","sc0pare","sc0pata","sch1ampe","sche1se","sche1sse","scheise","scheisse","schlampe","schwachs1nn1g","schwachs1nnig","schwachsinn1g","schwachsinnig","schwanz","scopare","scopata","sexy","sh1t","shit","slut","sp0mp1nare","sp0mpinare","spomp1nare","spompinare","str0nz0","str0nza","str0nzo","stronz0","stronza","stronzo","stup1d","stupid","succh1am1","succh1ami","succhiam1","succhiami","sucker","t0pa","tapette","test1c1e","test1cle","testic1e","testicle","tette","topa","tr01a","tr0ia","tr0mbare","tr1ng1er","tr1ngler","tring1er","tringler","tro1a","troia","trombare","turd","twat","vaffancu10","vaffancu1o","vaffancul0","vaffanculo","vag1na","vagina","verdammt","verga","w1chsen","wank","wichsen","x0ch0ta","x0chota","xana","xoch0ta","xochota","z0cc01a","z0cc0la","z0cco1a","z0ccola","z1z1","z1zi","ziz1","zizi","zocc01a","zocc0la","zocco1a","zoccola"])};class c{constructor(e){var a,c,n;const o=null!==(a=null==e?void 0:e.alphabet)&&void 0!==a?a:t.alphabet,r=null!==(c=null==e?void 0:e.minLength)&&void 0!==c?c:t.minLength,i=null!==(n=null==e?void 0:e.blocklist)&&void 0!==n?n:new Set(t.blocklist);if(o.length<5)throw new Error("Alphabet length must be at least 5");if(new Set(o).size!==o.length)throw new Error("Alphabet must contain unique characters");if("number"!=typeof r||r<this.minValue()||r>o.length)throw new TypeError(`Minimum length has to be between ${this.minValue()} and ${o.length}`);const s=new Set,l=o.toLowerCase().split("");for(const e of i)if(e.length>=3){const a=e.toLowerCase(),t=a.split("");t.filter((e=>l.includes(e))).length===t.length&&s.add(a)}this.alphabet=this.shuffle(o),this.minLength=r,this.blocklist=s}encode(e){if(0===e.length)return"";if(e.filter((e=>e>=this.minValue()&&e<=this.maxValue())).length!==e.length)throw new Error(`Encoding supports numbers between ${this.minValue()} and ${this.maxValue()}`);return this.encodeNumbers(e,!1)}decode(e){const a=[];if(""===e)return a;const t=this.alphabet.split("");for(const c of e.split(""))if(!t.includes(c))return a;const c=e.charAt(0),n=this.alphabet.indexOf(c);let o=this.alphabet.slice(n)+this.alphabet.slice(0,n);const r=o.charAt(1);o=o.slice(2);let i=e.slice(1);const s=i.indexOf(r);for(s>0&&s<i.length-1&&(i=i.slice(s+1),o=this.shuffle(o));i.length>0;){const e=o.slice(-1),t=i.split(e);if(t.length>0){const e=o.slice(0,-1);for(const a of t[0])if(!e.includes(a))return[];a.push(this.toNumber(t[0],e)),t.length>1&&(o=this.shuffle(o))}i=t.slice(1).join(e)}return a}minValue(){return 0}maxValue(){return Number.MAX_SAFE_INTEGER}encodeNumbers(e,a=!1){const t=e.reduce(((e,a,t)=>this.alphabet[a%this.alphabet.length].codePointAt(0)+t+e),e.length)%this.alphabet.length;let c=this.alphabet.slice(t)+this.alphabet.slice(0,t);const n=c.charAt(0),o=c.charAt(1);c=c.slice(2);const r=[n];for(let t=0;t!==e.length;t++){const n=e[t],i=c.slice(0,-1);if(r.push(this.toId(n,i)),t<e.length-1){const e=c.slice(-1);a&&0===t?r.push(o):r.push(e),c=this.shuffle(c)}}let i=r.join("");if(this.minLength>i.length){if(!a){const a=[0,...e];i=this.encodeNumbers(a,!0)}this.minLength>i.length&&(i=i.slice(0,1)+c.slice(0,this.minLength-i.length)+i.slice(1))}if(this.isBlockedId(i)){let t=e;if(a){if(e[0]+1>this.maxValue())throw new Error("Ran out of range checking against the blocklist");t[0]+=1}else t=[0,...e];i=this.encodeNumbers(t,!0)}return i}shuffle(e){const a=e.split("");for(let e=0,t=a.length-1;t>0;e++,t--){const c=(e*t+a[e].codePointAt(0)+a[t].codePointAt(0))%a.length;[a[e],a[c]]=[a[c],a[e]]}return a.join("")}toId(e,a){const t=[],c=a.split("");let n=e;do{t.unshift(c[n%c.length]),n=Math.floor(n/c.length)}while(n>0);return t.join("")}toNumber(e,a){const t=a.split("");return e.split("").reduce(((e,a)=>e*t.length+t.indexOf(a)),0)}isBlockedId(e){const a=e.toLowerCase();for(const e of this.blocklist)if(e.length<=a.length)if(a.length<=3||e.length<=3){if(a===e)return!0}else if(/\d/.test(e)){if(a.startsWith(e)||a.endsWith(e))return!0}else if(a.includes(e))return!0;return!1}}return a.default})())); | ||
!function(e,a){"object"==typeof exports&&"object"==typeof module?module.exports=a():"function"==typeof define&&define.amd?define("Sqids",[],a):"object"==typeof exports?exports.Sqids=a():e.Sqids=a()}(self,(()=>(()=>{"use strict";var e={d:(a,t)=>{for(var c in t)e.o(t,c)&&!e.o(a,c)&&Object.defineProperty(a,c,{enumerable:!0,get:t[c]})},o:(e,a)=>Object.prototype.hasOwnProperty.call(e,a)},a={};e.d(a,{default:()=>c});const t={alphabet:"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",minLength:0,blocklist:new Set(["0rgasm","1d10t","1d1ot","1di0t","1diot","1eccacu10","1eccacu1o","1eccacul0","1eccaculo","1mbec11e","1mbec1le","1mbeci1e","1mbecile","a11upat0","a11upato","a1lupat0","a1lupato","aand","ah01e","ah0le","aho1e","ahole","al1upat0","al1upato","allupat0","allupato","ana1","ana1e","anal","anale","anus","arrapat0","arrapato","arsch","arse","ass","b00b","b00be","b01ata","b0ceta","b0iata","b0ob","b0obe","b0sta","b1tch","b1te","b1tte","ba1atkar","balatkar","bastard0","bastardo","batt0na","battona","bitch","bite","bitte","bo0b","bo0be","bo1ata","boceta","boiata","boob","boobe","bosta","bran1age","bran1er","bran1ette","bran1eur","bran1euse","branlage","branler","branlette","branleur","branleuse","c0ck","c0g110ne","c0g11one","c0g1i0ne","c0g1ione","c0gl10ne","c0gl1one","c0gli0ne","c0glione","c0na","c0nnard","c0nnasse","c0nne","c0u111es","c0u11les","c0u1l1es","c0u1lles","c0ui11es","c0ui1les","c0uil1es","c0uilles","c11t","c11t0","c11to","c1it","c1it0","c1ito","cabr0n","cabra0","cabrao","cabron","caca","cacca","cacete","cagante","cagar","cagare","cagna","cara1h0","cara1ho","caracu10","caracu1o","caracul0","caraculo","caralh0","caralho","cazz0","cazz1mma","cazzata","cazzimma","cazzo","ch00t1a","ch00t1ya","ch00tia","ch00tiya","ch0d","ch0ot1a","ch0ot1ya","ch0otia","ch0otiya","ch1asse","ch1avata","ch1er","ch1ng0","ch1ngadaz0s","ch1ngadazos","ch1ngader1ta","ch1ngaderita","ch1ngar","ch1ngo","ch1ngues","ch1nk","chatte","chiasse","chiavata","chier","ching0","chingadaz0s","chingadazos","chingader1ta","chingaderita","chingar","chingo","chingues","chink","cho0t1a","cho0t1ya","cho0tia","cho0tiya","chod","choot1a","choot1ya","chootia","chootiya","cl1t","cl1t0","cl1to","clit","clit0","clito","cock","cog110ne","cog11one","cog1i0ne","cog1ione","cogl10ne","cogl1one","cogli0ne","coglione","cona","connard","connasse","conne","cou111es","cou11les","cou1l1es","cou1lles","coui11es","coui1les","couil1es","couilles","cracker","crap","cu10","cu1att0ne","cu1attone","cu1er0","cu1ero","cu1o","cul0","culatt0ne","culattone","culer0","culero","culo","cum","cunt","d11d0","d11do","d1ck","d1ld0","d1ldo","damn","de1ch","deich","depp","di1d0","di1do","dick","dild0","dildo","dyke","encu1e","encule","enema","enf01re","enf0ire","enfo1re","enfoire","estup1d0","estup1do","estupid0","estupido","etr0n","etron","f0da","f0der","f0ttere","f0tters1","f0ttersi","f0tze","f0utre","f1ca","f1cker","f1ga","fag","fica","ficker","figa","foda","foder","fottere","fotters1","fottersi","fotze","foutre","fr0c10","fr0c1o","fr0ci0","fr0cio","fr0sc10","fr0sc1o","fr0sci0","fr0scio","froc10","froc1o","froci0","frocio","frosc10","frosc1o","frosci0","froscio","fuck","g00","g0o","g0u1ne","g0uine","gandu","go0","goo","gou1ne","gouine","gr0gnasse","grognasse","haram1","harami","haramzade","hund1n","hundin","id10t","id1ot","idi0t","idiot","imbec11e","imbec1le","imbeci1e","imbecile","j1zz","jerk","jizz","k1ke","kam1ne","kamine","kike","leccacu10","leccacu1o","leccacul0","leccaculo","m1erda","m1gn0tta","m1gnotta","m1nch1a","m1nchia","m1st","mam0n","mamahuev0","mamahuevo","mamon","masturbat10n","masturbat1on","masturbate","masturbati0n","masturbation","merd0s0","merd0so","merda","merde","merdos0","merdoso","mierda","mign0tta","mignotta","minch1a","minchia","mist","musch1","muschi","n1gger","neger","negr0","negre","negro","nerch1a","nerchia","nigger","orgasm","p00p","p011a","p01la","p0l1a","p0lla","p0mp1n0","p0mp1no","p0mpin0","p0mpino","p0op","p0rca","p0rn","p0rra","p0uff1asse","p0uffiasse","p1p1","p1pi","p1r1a","p1rla","p1sc10","p1sc1o","p1sci0","p1scio","p1sser","pa11e","pa1le","pal1e","palle","pane1e1r0","pane1e1ro","pane1eir0","pane1eiro","panele1r0","panele1ro","paneleir0","paneleiro","patakha","pec0r1na","pec0rina","pecor1na","pecorina","pen1s","pendej0","pendejo","penis","pip1","pipi","pir1a","pirla","pisc10","pisc1o","pisci0","piscio","pisser","po0p","po11a","po1la","pol1a","polla","pomp1n0","pomp1no","pompin0","pompino","poop","porca","porn","porra","pouff1asse","pouffiasse","pr1ck","prick","pussy","put1za","puta","puta1n","putain","pute","putiza","puttana","queca","r0mp1ba11e","r0mp1ba1le","r0mp1bal1e","r0mp1balle","r0mpiba11e","r0mpiba1le","r0mpibal1e","r0mpiballe","rand1","randi","rape","recch10ne","recch1one","recchi0ne","recchione","retard","romp1ba11e","romp1ba1le","romp1bal1e","romp1balle","rompiba11e","rompiba1le","rompibal1e","rompiballe","ruff1an0","ruff1ano","ruffian0","ruffiano","s1ut","sa10pe","sa1aud","sa1ope","sacanagem","sal0pe","salaud","salope","saugnapf","sb0rr0ne","sb0rra","sb0rrone","sbattere","sbatters1","sbattersi","sborr0ne","sborra","sborrone","sc0pare","sc0pata","sch1ampe","sche1se","sche1sse","scheise","scheisse","schlampe","schwachs1nn1g","schwachs1nnig","schwachsinn1g","schwachsinnig","schwanz","scopare","scopata","sexy","sh1t","shit","slut","sp0mp1nare","sp0mpinare","spomp1nare","spompinare","str0nz0","str0nza","str0nzo","stronz0","stronza","stronzo","stup1d","stupid","succh1am1","succh1ami","succhiam1","succhiami","sucker","t0pa","tapette","test1c1e","test1cle","testic1e","testicle","tette","topa","tr01a","tr0ia","tr0mbare","tr1ng1er","tr1ngler","tring1er","tringler","tro1a","troia","trombare","turd","twat","vaffancu10","vaffancu1o","vaffancul0","vaffanculo","vag1na","vagina","verdammt","verga","w1chsen","wank","wichsen","x0ch0ta","x0chota","xana","xoch0ta","xochota","z0cc01a","z0cc0la","z0cco1a","z0ccola","z1z1","z1zi","ziz1","zizi","zocc01a","zocc0la","zocco1a","zoccola"])};class c{constructor(e){var a,c,n;const o=null!==(a=null==e?void 0:e.alphabet)&&void 0!==a?a:t.alphabet,r=null!==(c=null==e?void 0:e.minLength)&&void 0!==c?c:t.minLength,i=null!==(n=null==e?void 0:e.blocklist)&&void 0!==n?n:t.blocklist;if(new Blob([o]).size!==o.length)throw new Error("Alphabet cannot contain multibyte characters");if(o.length<3)throw new Error("Alphabet length must be at least 3");if(new Set(o).size!==o.length)throw new Error("Alphabet must contain unique characters");if("number"!=typeof r||r<0||r>255)throw new Error("Minimum length has to be between 0 and 255");const s=new Set,l=o.toLowerCase().split("");for(const e of i)if(e.length>=3){const a=e.toLowerCase(),t=a.split("");t.filter((e=>l.includes(e))).length===t.length&&s.add(a)}this.alphabet=this.shuffle(o),this.minLength=r,this.blocklist=s}encode(e){if(0===e.length)return"";if(e.filter((e=>e>=0&&e<=this.maxValue())).length!==e.length)throw new Error(`Encoding supports numbers between 0 and ${this.maxValue()}`);return this.encodeNumbers(e)}decode(e){const a=[];if(""===e)return a;const t=this.alphabet.split("");for(const c of e.split(""))if(!t.includes(c))return a;const c=e.charAt(0),n=this.alphabet.indexOf(c);let o=this.alphabet.slice(n)+this.alphabet.slice(0,n);o=o.split("").reverse().join("");let r=e.slice(1);for(;r.length>0;){const e=o.slice(0,1),t=r.split(e);if(t.length>0){if(""===t[0])return a;a.push(this.toNumber(t[0],o.slice(1))),t.length>1&&(o=this.shuffle(o))}r=t.slice(1).join(e)}return a}encodeNumbers(e,a=0){if(a>this.alphabet.length)throw new Error("Reached max attempts to re-generate the ID");let t=e.reduce(((e,a,t)=>this.alphabet[a%this.alphabet.length].codePointAt(0)+t+e),e.length)%this.alphabet.length;t=(t+a)%this.alphabet.length;let c=this.alphabet.slice(t)+this.alphabet.slice(0,t);const n=c.charAt(0);c=c.split("").reverse().join("");const o=[n];for(let a=0;a!==e.length;a++){const t=e[a];o.push(this.toId(t,c.slice(1))),a<e.length-1&&(o.push(c.slice(0,1)),c=this.shuffle(c))}let r=o.join("");if(this.minLength>r.length)for(r+=c.slice(0,1);this.minLength-r.length>0;)c=this.shuffle(c),r+=c.slice(0,Math.min(this.minLength-r.length,c.length));return this.isBlockedId(r)&&(r=this.encodeNumbers(e,a+1)),r}shuffle(e){const a=e.split("");for(let e=0,t=a.length-1;t>0;e++,t--){const c=(e*t+a[e].codePointAt(0)+a[t].codePointAt(0))%a.length;[a[e],a[c]]=[a[c],a[e]]}return a.join("")}toId(e,a){const t=[],c=a.split("");let n=e;do{t.unshift(c[n%c.length]),n=Math.floor(n/c.length)}while(n>0);return t.join("")}toNumber(e,a){const t=a.split("");return e.split("").reduce(((e,a)=>e*t.length+t.indexOf(a)),0)}isBlockedId(e){const a=e.toLowerCase();for(const e of this.blocklist)if(e.length<=a.length)if(a.length<=3||e.length<=3){if(a===e)return!0}else if(/\d/.test(e)){if(a.startsWith(e)||a.endsWith(e))return!0}else if(a.includes(e))return!0;return!1}maxValue(){return Number.MAX_SAFE_INTEGER}}return a.default})())); | ||
//# sourceMappingURL=sqids.js.map |
@@ -18,4 +18,2 @@ interface SqidsOptions { | ||
decode(id: string): number[]; | ||
minValue(): number; | ||
maxValue(): number; | ||
private encodeNumbers; | ||
@@ -26,3 +24,4 @@ private shuffle; | ||
private isBlockedId; | ||
private maxValue; | ||
} | ||
export {}; |
@@ -572,6 +572,9 @@ export const defaultOptions = { | ||
const minLength = (_b = options === null || options === void 0 ? void 0 : options.minLength) !== null && _b !== void 0 ? _b : defaultOptions.minLength; | ||
const blocklist = (_c = options === null || options === void 0 ? void 0 : options.blocklist) !== null && _c !== void 0 ? _c : new Set(defaultOptions.blocklist); | ||
const minAlphabetLength = 5; | ||
const blocklist = (_c = options === null || options === void 0 ? void 0 : options.blocklist) !== null && _c !== void 0 ? _c : defaultOptions.blocklist; | ||
if (new Blob([alphabet]).size !== alphabet.length) { | ||
throw new Error('Alphabet cannot contain multibyte characters'); | ||
} | ||
const minAlphabetLength = 3; | ||
if (alphabet.length < minAlphabetLength) { | ||
throw new Error('Alphabet length must be at least 5'); | ||
throw new Error(`Alphabet length must be at least ${minAlphabetLength}`); | ||
} | ||
@@ -581,6 +584,7 @@ if (new Set(alphabet).size !== alphabet.length) { | ||
} | ||
const minLengthLimit = 255; | ||
if (typeof minLength !== 'number' || | ||
minLength < this.minValue() || | ||
minLength > alphabet.length) { | ||
throw new TypeError(`Minimum length has to be between ${this.minValue()} and ${alphabet.length}`); | ||
minLength < 0 || | ||
minLength > minLengthLimit) { | ||
throw new Error(`Minimum length has to be between 0 and ${minLengthLimit}`); | ||
} | ||
@@ -607,7 +611,7 @@ const filteredBlocklist = new Set(); | ||
} | ||
const inRangeNumbers = numbers.filter((n) => n >= this.minValue() && n <= this.maxValue()); | ||
const inRangeNumbers = numbers.filter((n) => n >= 0 && n <= this.maxValue()); | ||
if (inRangeNumbers.length !== numbers.length) { | ||
throw new Error(`Encoding supports numbers between ${this.minValue()} and ${this.maxValue()}`); | ||
throw new Error(`Encoding supports numbers between 0 and ${this.maxValue()}`); | ||
} | ||
return this.encodeNumbers(numbers, false); | ||
return this.encodeNumbers(numbers); | ||
} | ||
@@ -628,21 +632,12 @@ decode(id) { | ||
let alphabet = this.alphabet.slice(offset) + this.alphabet.slice(0, offset); | ||
const partition = alphabet.charAt(1); | ||
alphabet = alphabet.slice(2); | ||
alphabet = alphabet.split('').reverse().join(''); | ||
let slicedId = id.slice(1); | ||
const partitionIndex = slicedId.indexOf(partition); | ||
if (partitionIndex > 0 && partitionIndex < slicedId.length - 1) { | ||
slicedId = slicedId.slice(partitionIndex + 1); | ||
alphabet = this.shuffle(alphabet); | ||
} | ||
while (slicedId.length > 0) { | ||
const separator = alphabet.slice(-1); | ||
const separator = alphabet.slice(0, 1); | ||
const chunks = slicedId.split(separator); | ||
if (chunks.length > 0) { | ||
const alphabetWithoutSeparator = alphabet.slice(0, -1); | ||
for (const c of chunks[0]) { | ||
if (!alphabetWithoutSeparator.includes(c)) { | ||
return []; | ||
} | ||
if (chunks[0] === '') { | ||
return ret; | ||
} | ||
ret.push(this.toNumber(chunks[0], alphabetWithoutSeparator)); | ||
ret.push(this.toNumber(chunks[0], alphabet.slice(1))); | ||
if (chunks.length > 1) { | ||
@@ -656,27 +651,17 @@ alphabet = this.shuffle(alphabet); | ||
} | ||
minValue() { | ||
return 0; | ||
} | ||
maxValue() { | ||
return Number.MAX_SAFE_INTEGER; | ||
} | ||
encodeNumbers(numbers, partitioned = false) { | ||
const offset = numbers.reduce((a, v, i) => this.alphabet[v % this.alphabet.length].codePointAt(0) + i + a, numbers.length) % this.alphabet.length; | ||
encodeNumbers(numbers, increment = 0) { | ||
if (increment > this.alphabet.length) { | ||
throw new Error('Reached max attempts to re-generate the ID'); | ||
} | ||
let offset = numbers.reduce((a, v, i) => this.alphabet[v % this.alphabet.length].codePointAt(0) + i + a, numbers.length) % this.alphabet.length; | ||
offset = (offset + increment) % this.alphabet.length; | ||
let alphabet = this.alphabet.slice(offset) + this.alphabet.slice(0, offset); | ||
const prefix = alphabet.charAt(0); | ||
const partition = alphabet.charAt(1); | ||
alphabet = alphabet.slice(2); | ||
alphabet = alphabet.split('').reverse().join(''); | ||
const ret = [prefix]; | ||
for (let i = 0; i !== numbers.length; i++) { | ||
const num = numbers[i]; | ||
const alphabetWithoutSeparator = alphabet.slice(0, -1); | ||
ret.push(this.toId(num, alphabetWithoutSeparator)); | ||
ret.push(this.toId(num, alphabet.slice(1))); | ||
if (i < numbers.length - 1) { | ||
const separator = alphabet.slice(-1); | ||
if (partitioned && i === 0) { | ||
ret.push(partition); | ||
} | ||
else { | ||
ret.push(separator); | ||
} | ||
ret.push(alphabet.slice(0, 1)); | ||
alphabet = this.shuffle(alphabet); | ||
@@ -687,27 +672,10 @@ } | ||
if (this.minLength > id.length) { | ||
if (!partitioned) { | ||
const newNumbers = [0, ...numbers]; | ||
id = this.encodeNumbers(newNumbers, true); | ||
id += alphabet.slice(0, 1); | ||
while (this.minLength - id.length > 0) { | ||
alphabet = this.shuffle(alphabet); | ||
id += alphabet.slice(0, Math.min(this.minLength - id.length, alphabet.length)); | ||
} | ||
if (this.minLength > id.length) { | ||
id = | ||
id.slice(0, 1) + | ||
alphabet.slice(0, this.minLength - id.length) + | ||
id.slice(1); | ||
} | ||
} | ||
if (this.isBlockedId(id)) { | ||
let newNumbers = numbers; | ||
if (partitioned) { | ||
if (numbers[0] + 1 > this.maxValue()) { | ||
throw new Error('Ran out of range checking against the blocklist'); | ||
} | ||
else { | ||
newNumbers[0] += 1; | ||
} | ||
} | ||
else { | ||
newNumbers = [0, ...numbers]; | ||
} | ||
id = this.encodeNumbers(newNumbers, true); | ||
id = this.encodeNumbers(numbers, increment + 1); | ||
} | ||
@@ -760,3 +728,6 @@ return id; | ||
} | ||
maxValue() { | ||
return Number.MAX_SAFE_INTEGER; | ||
} | ||
} | ||
//# sourceMappingURL=sqids.js.map |
{ | ||
"name": "sqids", | ||
"version": "0.1.3", | ||
"version": "0.3.0", | ||
"description": "Generate YouTube-like ids from numbers.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -45,3 +45,3 @@ # [Sqids JavaScript](https://sqids.org/javascript) | ||
const sqids = new Sqids() | ||
const id = sqids.encode([1, 2, 3]) // "8QRLaD" | ||
const id = sqids.encode([1, 2, 3]) // "86Rf07" | ||
const numbers = sqids.decode(id) // [1, 2, 3] | ||
@@ -53,19 +53,19 @@ ``` | ||
Randomize IDs by providing a custom alphabet: | ||
Enforce a *minimum* length for IDs: | ||
```javascript | ||
const sqids = new Sqids({ | ||
alphabet: 'FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE', | ||
minLength: 10, | ||
}) | ||
const id = sqids.encode([1, 2, 3]) // "B5aMa3" | ||
const id = sqids.encode([1, 2, 3]) // "86Rf07xd4z" | ||
const numbers = sqids.decode(id) // [1, 2, 3] | ||
``` | ||
Enforce a *minimum* length for IDs: | ||
Randomize IDs by providing a custom alphabet: | ||
```javascript | ||
const sqids = new Sqids({ | ||
minLength: 10, | ||
alphabet: 'FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE', | ||
}) | ||
const id = sqids.encode([1, 2, 3]) // "75JT1cd0dL" | ||
const id = sqids.encode([1, 2, 3]) // "B4aajs" | ||
const numbers = sqids.decode(id) // [1, 2, 3] | ||
@@ -78,5 +78,5 @@ ``` | ||
const sqids = new Sqids({ | ||
blocklist: new Set(['word1', 'word2']), | ||
blocklist: new Set(['86Rf07']), | ||
}) | ||
const id = sqids.encode([1, 2, 3]) // "8QRLaD" | ||
const id = sqids.encode([1, 2, 3]) // "se8ojk" | ||
const numbers = sqids.decode(id) // [1, 2, 3] | ||
@@ -83,0 +83,0 @@ ``` |
119
src/sqids.ts
@@ -582,8 +582,11 @@ interface SqidsOptions { | ||
const minLength = options?.minLength ?? defaultOptions.minLength | ||
const blocklist = | ||
options?.blocklist ?? new Set<string>(defaultOptions.blocklist) | ||
const blocklist = options?.blocklist ?? defaultOptions.blocklist | ||
const minAlphabetLength = 5 | ||
if (new Blob([alphabet]).size !== alphabet.length) { | ||
throw new Error('Alphabet cannot contain multibyte characters') | ||
} | ||
const minAlphabetLength = 3 | ||
if (alphabet.length < minAlphabetLength) { | ||
throw new Error('Alphabet length must be at least 5') | ||
throw new Error(`Alphabet length must be at least ${minAlphabetLength}`) | ||
} | ||
@@ -595,11 +598,10 @@ | ||
const minLengthLimit = 255 | ||
if ( | ||
typeof minLength !== 'number' || | ||
minLength < this.minValue() || | ||
minLength > alphabet.length | ||
minLength < 0 || | ||
minLength > minLengthLimit | ||
) { | ||
throw new TypeError( | ||
`Minimum length has to be between ${this.minValue()} and ${ | ||
alphabet.length | ||
}`, | ||
throw new Error( | ||
`Minimum length has to be between 0 and ${minLengthLimit}`, | ||
) | ||
@@ -631,12 +633,10 @@ } | ||
const inRangeNumbers = numbers.filter( | ||
(n) => n >= this.minValue() && n <= this.maxValue(), | ||
) | ||
const inRangeNumbers = numbers.filter((n) => n >= 0 && n <= this.maxValue()) | ||
if (inRangeNumbers.length !== numbers.length) { | ||
throw new Error( | ||
`Encoding supports numbers between ${this.minValue()} and ${this.maxValue()}`, | ||
`Encoding supports numbers between 0 and ${this.maxValue()}`, | ||
) | ||
} | ||
return this.encodeNumbers(numbers, false) | ||
return this.encodeNumbers(numbers) | ||
} | ||
@@ -661,25 +661,15 @@ | ||
let alphabet = this.alphabet.slice(offset) + this.alphabet.slice(0, offset) | ||
const partition = alphabet.charAt(1) | ||
alphabet = alphabet.slice(2) | ||
alphabet = alphabet.split('').reverse().join('') | ||
let slicedId = id.slice(1) | ||
const partitionIndex = slicedId.indexOf(partition) | ||
if (partitionIndex > 0 && partitionIndex < slicedId.length - 1) { | ||
slicedId = slicedId.slice(partitionIndex + 1) | ||
alphabet = this.shuffle(alphabet) | ||
} | ||
while (slicedId.length > 0) { | ||
const separator = alphabet.slice(-1) | ||
const separator = alphabet.slice(0, 1) | ||
const chunks = slicedId.split(separator) | ||
if (chunks.length > 0) { | ||
const alphabetWithoutSeparator = alphabet.slice(0, -1) | ||
for (const c of chunks[0]!) { | ||
if (!alphabetWithoutSeparator.includes(c)) { | ||
return [] | ||
} | ||
if (chunks[0] === '') { | ||
return ret | ||
} | ||
ret.push(this.toNumber(chunks[0]!, alphabetWithoutSeparator)) | ||
ret.push(this.toNumber(chunks[0]!, alphabet.slice(1))) | ||
if (chunks.length > 1) { | ||
@@ -696,12 +686,8 @@ alphabet = this.shuffle(alphabet) | ||
minValue() { | ||
return 0 | ||
} | ||
private encodeNumbers(numbers: number[], increment = 0): string { | ||
if (increment > this.alphabet.length) { | ||
throw new Error('Reached max attempts to re-generate the ID') | ||
} | ||
maxValue() { | ||
return Number.MAX_SAFE_INTEGER | ||
} | ||
private encodeNumbers(numbers: number[], partitioned = false): string { | ||
const offset = | ||
let offset = | ||
numbers.reduce( | ||
@@ -713,10 +699,6 @@ (a, v, i) => | ||
offset = (offset + increment) % this.alphabet.length | ||
let alphabet = this.alphabet.slice(offset) + this.alphabet.slice(0, offset) | ||
const prefix = alphabet.charAt(0) | ||
const partition = alphabet.charAt(1) | ||
alphabet = alphabet.slice(2) | ||
alphabet = alphabet.split('').reverse().join('') | ||
const ret = [prefix] | ||
@@ -727,14 +709,5 @@ | ||
const alphabetWithoutSeparator = alphabet.slice(0, -1) | ||
ret.push(this.toId(num, alphabetWithoutSeparator)) | ||
ret.push(this.toId(num, alphabet.slice(1))) | ||
if (i < numbers.length - 1) { | ||
const separator = alphabet.slice(-1) | ||
if (partitioned && i === 0) { | ||
ret.push(partition) | ||
} else { | ||
ret.push(separator) | ||
} | ||
ret.push(alphabet.slice(0, 1)) | ||
alphabet = this.shuffle(alphabet) | ||
@@ -747,12 +720,10 @@ } | ||
if (this.minLength > id.length) { | ||
if (!partitioned) { | ||
const newNumbers = [0, ...numbers] | ||
id = this.encodeNumbers(newNumbers, true) | ||
} | ||
id += alphabet.slice(0, 1) | ||
if (this.minLength > id.length) { | ||
id = | ||
id.slice(0, 1) + | ||
alphabet.slice(0, this.minLength - id.length) + | ||
id.slice(1) | ||
while (this.minLength - id.length > 0) { | ||
alphabet = this.shuffle(alphabet) | ||
id += alphabet.slice( | ||
0, | ||
Math.min(this.minLength - id.length, alphabet.length), | ||
) | ||
} | ||
@@ -762,15 +733,3 @@ } | ||
if (this.isBlockedId(id)) { | ||
let newNumbers = numbers | ||
if (partitioned) { | ||
if (numbers[0]! + 1 > this.maxValue()) { | ||
throw new Error('Ran out of range checking against the blocklist') | ||
} else { | ||
newNumbers[0] += 1 | ||
} | ||
} else { | ||
newNumbers = [0, ...numbers] | ||
} | ||
id = this.encodeNumbers(newNumbers, true) | ||
id = this.encodeNumbers(numbers, increment + 1) | ||
} | ||
@@ -834,2 +793,6 @@ | ||
} | ||
private maxValue() { | ||
return Number.MAX_SAFE_INTEGER | ||
} | ||
} |
@@ -10,3 +10,3 @@ import Sqids from '../sqids' | ||
const numbers = [1, 2, 3] | ||
const id = '4d9fd2' | ||
const id = '489158' | ||
@@ -19,3 +19,3 @@ expect(sqids.encode(numbers)).toBe(id) | ||
const sqids = new Sqids({ | ||
alphabet: 'abcde', | ||
alphabet: 'abc', | ||
}) | ||
@@ -37,2 +37,11 @@ | ||
it('multibyte characters', () => { | ||
expect( | ||
() => | ||
new Sqids({ | ||
alphabet: 'ë1092', | ||
}), | ||
).toThrow('Alphabet cannot contain multibyte characters') | ||
}) | ||
it('repeating alphabet characters', () => { | ||
@@ -51,6 +60,6 @@ expect( | ||
new Sqids({ | ||
alphabet: 'abcd', | ||
alphabet: 'ab', | ||
}), | ||
).toThrow('Alphabet length must be at least 5') | ||
).toThrow('Alphabet length must be at least 3') | ||
}) | ||
}) |
@@ -7,4 +7,4 @@ import Sqids from '../sqids' | ||
expect(sqids.decode('sexy')).toEqual([200_044]) | ||
expect(sqids.encode([200_044])).toBe('d171vI') | ||
expect(sqids.decode('aho1e')).toEqual([4_572_721]) | ||
expect(sqids.encode([4_572_721])).toBe('JExTR') | ||
}) | ||
@@ -17,4 +17,4 @@ | ||
expect(sqids.decode('sexy')).toEqual([200_044]) | ||
expect(sqids.encode([200_044])).toBe('sexy') | ||
expect(sqids.decode('aho1e')).toEqual([4_572_721]) | ||
expect(sqids.encode([4_572_721])).toBe('aho1e') | ||
}) | ||
@@ -25,3 +25,3 @@ | ||
blocklist: new Set([ | ||
'AvTg', // originally encoded [100000] | ||
'ArUO', // originally encoded [100000] | ||
]), | ||
@@ -31,9 +31,9 @@ }) | ||
// make sure we don't use the default blocklist | ||
expect(sqids.decode('sexy')).toEqual([200_044]) | ||
expect(sqids.encode([200_044])).toBe('sexy') | ||
expect(sqids.decode('aho1e')).toEqual([4_572_721]) | ||
expect(sqids.encode([4_572_721])).toBe('aho1e') | ||
// make sure we are using the passed blocklist | ||
expect(sqids.decode('AvTg')).toEqual([100_000]) | ||
expect(sqids.encode([100_000])).toBe('7T1X8k') | ||
expect(sqids.decode('7T1X8k')).toEqual([100_000]) | ||
expect(sqids.decode('ArUO')).toEqual([100_000]) | ||
expect(sqids.encode([100_000])).toBe('QyG4') | ||
expect(sqids.decode('QyG4')).toEqual([100_000]) | ||
}) | ||
@@ -44,12 +44,12 @@ | ||
blocklist: new Set([ | ||
'8QRLaD', // normal result of 1st encoding, let's block that word on purpose | ||
'7T1cd0dL', // result of 2nd encoding | ||
'UeIe', // result of 3rd encoding is `RA8UeIe7`, let's block a substring | ||
'imhw', // result of 4th encoding is `WM3Limhw`, let's block the postfix | ||
'LfUQ', // result of 4th encoding is `LfUQh4HN`, let's block the prefix | ||
'JSwXFaosAN', // normal result of 1st encoding, let's block that word on purpose | ||
'OCjV9JK64o', // result of 2nd encoding | ||
'rBHf', // result of 3rd encoding is `4rBHfOiqd3`, let's block a substring | ||
'79SM', // result of 4th encoding is `dyhgw479SM`, let's block the postfix | ||
'7tE6', // result of 4th encoding is `7tE6jdAHLe`, let's block the prefix | ||
]), | ||
}) | ||
expect(sqids.encode([1, 2, 3])).toBe('TM0x1Mxz') | ||
expect(sqids.decode('TM0x1Mxz')).toEqual([1, 2, 3]) | ||
expect(sqids.encode([1_000_000, 2_000_000])).toBe('1aYeB7bRUt') | ||
expect(sqids.decode('1aYeB7bRUt')).toEqual([1_000_000, 2_000_000]) | ||
}) | ||
@@ -59,16 +59,10 @@ | ||
const sqids = new Sqids({ | ||
blocklist: new Set([ | ||
'8QRLaD', | ||
'7T1cd0dL', | ||
'RA8UeIe7', | ||
'WM3Limhw', | ||
'LfUQh4HN', | ||
]), | ||
blocklist: new Set(['86Rf07', 'se8ojk', 'ARsz1p', 'Q8AI49', '5sQRZO']), | ||
}) | ||
expect(sqids.decode('8QRLaD')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('7T1cd0dL')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('RA8UeIe7')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('WM3Limhw')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('LfUQh4HN')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('86Rf07')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('se8ojk')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('ARsz1p')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('Q8AI49')).toEqual([1, 2, 3]) | ||
expect(sqids.decode('5sQRZO')).toEqual([1, 2, 3]) | ||
}) | ||
@@ -78,3 +72,3 @@ | ||
const sqids = new Sqids({ | ||
blocklist: new Set(['pPQ']), | ||
blocklist: new Set(['pnd']), | ||
}) | ||
@@ -88,3 +82,3 @@ | ||
alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', | ||
blocklist: new Set(['sqnmpn']), // lowercase blocklist in only-uppercase alphabet | ||
blocklist: new Set(['sxnzkl']), // lowercase blocklist in only-uppercase alphabet | ||
}) | ||
@@ -95,5 +89,24 @@ | ||
expect(id).toBe('ULPBZGBM') // without blocklist, would've been "SQNMPN" | ||
expect(id).toBe('IBSHOZ') // without blocklist, would've been "SXNZKL" | ||
expect(numbers).toEqual([1, 2, 3]) | ||
}) | ||
it('max encoding attempts', () => { | ||
const alphabet = 'abc' | ||
const minLength = 3 | ||
const blocklist = new Set(['cab', 'abc', 'bca']) | ||
const sqids = new Sqids({ | ||
alphabet, | ||
minLength, | ||
blocklist, | ||
}) | ||
expect(alphabet).toHaveLength(minLength) | ||
expect(blocklist.size).toEqual(minLength) | ||
expect(() => sqids.encode([0])).toThrow( | ||
'Reached max attempts to re-generate the ID', | ||
) | ||
}) | ||
}) |
@@ -8,3 +8,3 @@ import Sqids from '../sqids' | ||
const numbers = [1, 2, 3] | ||
const id = '8QRLaD' | ||
const id = '86Rf07' | ||
@@ -16,4 +16,2 @@ expect(sqids.encode(numbers)).toBe(id) | ||
it('different inputs', () => { | ||
const sqids = new Sqids() | ||
const numbers = [ | ||
@@ -30,4 +28,6 @@ 0, | ||
1_000_000, | ||
sqids.maxValue(), | ||
Number.MAX_SAFE_INTEGER, | ||
] | ||
const sqids = new Sqids() | ||
expect(sqids.decode(sqids.encode(numbers))).toEqual(numbers) | ||
@@ -40,12 +40,12 @@ }) | ||
const ids = { | ||
bV: [0], | ||
U9: [1], | ||
g8: [2], | ||
Ez: [3], | ||
V8: [4], | ||
ul: [5], | ||
O3: [6], | ||
AF: [7], | ||
ph: [8], | ||
n8: [9], | ||
bM: [0], | ||
Uk: [1], | ||
gb: [2], | ||
Ef: [3], | ||
Vq: [4], | ||
uw: [5], | ||
OI: [6], | ||
AX: [7], | ||
p6: [8], | ||
nJ: [9], | ||
} | ||
@@ -63,12 +63,12 @@ | ||
const ids = { | ||
SrIu: [0, 0], | ||
nZqE: [0, 1], | ||
tJyf: [0, 2], | ||
e86S: [0, 3], | ||
rtC7: [0, 4], | ||
sQ8R: [0, 5], | ||
uz2n: [0, 6], | ||
'7Td9': [0, 7], | ||
'3nWE': [0, 8], | ||
mIxM: [0, 9], | ||
SvIz: [0, 0], | ||
n3qa: [0, 1], | ||
tryF: [0, 2], | ||
eg6q: [0, 3], | ||
rSCF: [0, 4], | ||
sR8x: [0, 5], | ||
uY2M: [0, 6], | ||
'74dI': [0, 7], | ||
'30WX': [0, 8], | ||
moxr: [0, 9], | ||
} | ||
@@ -86,12 +86,12 @@ | ||
const ids = { | ||
SrIu: [0, 0], | ||
nbqh: [1, 0], | ||
t4yj: [2, 0], | ||
eQ6L: [3, 0], | ||
r4Cc: [4, 0], | ||
sL82: [5, 0], | ||
uo2f: [6, 0], | ||
'7Zdq': [7, 0], | ||
'36Wf': [8, 0], | ||
m4xT: [9, 0], | ||
SvIz: [0, 0], | ||
nWqP: [1, 0], | ||
tSyw: [2, 0], | ||
eX68: [3, 0], | ||
rxCY: [4, 0], | ||
sV8a: [5, 0], | ||
uf2K: [6, 0], | ||
'7Cdk': [7, 0], | ||
'3aWP': [8, 0], | ||
m2xn: [9, 0], | ||
} | ||
@@ -135,14 +135,11 @@ | ||
it('decoding an invalid ID with a repeating reserved character', () => { | ||
const sqids = new Sqids() | ||
expect(sqids.decode('fff')).toEqual([]) | ||
}) | ||
it('encode out-of-range numbers', () => { | ||
const sqids = new Sqids() | ||
const err = `Encoding supports numbers between ${sqids.minValue()} and ${sqids.maxValue()}` | ||
const encodingError = `Encoding supports numbers between 0 and ${Number.MAX_SAFE_INTEGER}` | ||
expect(() => sqids.encode([sqids.minValue() - 1])).toThrow(err) | ||
expect(() => sqids.encode([sqids.maxValue() + 1])).toThrow(err) | ||
expect(() => sqids.encode([-1])).toThrow(encodingError) | ||
expect(() => sqids.encode([Number.MAX_SAFE_INTEGER + 1])).toThrow( | ||
encodingError, | ||
) | ||
}) | ||
}) |
@@ -10,3 +10,3 @@ import Sqids, { defaultOptions } from '../sqids' | ||
const numbers = [1, 2, 3] | ||
const id = '75JILToVsGerOADWmHlY38xvbaNZKQ9wdFS0B6kcMEtnRpgizhjU42qT1cd0dL' | ||
const id = '86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM' | ||
@@ -17,2 +17,35 @@ expect(sqids.encode(numbers)).toBe(id) | ||
it('incremental', () => { | ||
const numbers = [1, 2, 3] | ||
const map = { | ||
6: '86Rf07', | ||
7: '86Rf07x', | ||
8: '86Rf07xd', | ||
9: '86Rf07xd4', | ||
10: '86Rf07xd4z', | ||
11: '86Rf07xd4zB', | ||
12: '86Rf07xd4zBm', | ||
13: '86Rf07xd4zBmi', | ||
[defaultOptions.alphabet.length + 0]: | ||
'86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM', | ||
[defaultOptions.alphabet.length + 1]: | ||
'86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMy', | ||
[defaultOptions.alphabet.length + 2]: | ||
'86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf', | ||
[defaultOptions.alphabet.length + 3]: | ||
'86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf1', | ||
} | ||
for (const [minLength, id] of Object.entries(map)) { | ||
const sqids = new Sqids({ | ||
minLength: Number(minLength), | ||
}) | ||
expect(sqids.encode(numbers)).toBe(id) | ||
expect(sqids.encode(numbers)).toHaveLength(Number(minLength)) | ||
expect(sqids.decode(id)).toEqual(numbers) | ||
} | ||
}) | ||
it('incremental numbers', () => { | ||
@@ -24,12 +57,12 @@ const sqids = new Sqids({ | ||
const ids = { | ||
jf26PLNeO5WbJDUV7FmMtlGXps3CoqkHnZ8cYd19yIiTAQuvKSExzhrRghBlwf: [0, 0], | ||
vQLUq7zWXC6k9cNOtgJ2ZK8rbxuipBFAS10yTdYeRa3ojHwGnmMV4PDhESI2jL: [0, 1], | ||
YhcpVK3COXbifmnZoLuxWgBQwtjsSaDGAdr0ReTHM16yI9vU8JNzlFq5Eu2oPp: [0, 2], | ||
OTkn9daFgDZX6LbmfxI83RSKetJu0APihlsrYoz5pvQw7GyWHEUcN2jBqd4kJ9: [0, 3], | ||
h2cV5eLNYj1x4ToZpfM90UlgHBOKikQFvnW36AC8zrmuJ7XdRytIGPawqYEbBe: [0, 4], | ||
'7Mf0HeUNkpsZOTvmcj836P9EWKaACBubInFJtwXR2DSzgYGhQV5i4lLxoT1qdU': [0, 5], | ||
APVSD1ZIY4WGBK75xktMfTev8qsCJw6oyH2j3OnLcXRlhziUmpbuNEar05QCsI: [0, 6], | ||
P0LUhnlT76rsWSofOeyRGQZv1cC5qu3dtaJYNEXwk8Vpx92bKiHIz4MgmiDOF7: [0, 7], | ||
xAhypZMXYIGCL4uW0te6lsFHaPc3SiD1TBgw5O7bvodzjqUn89JQRfk2Nvm4JI: [0, 8], | ||
'94dRPIZ6irlXWvTbKywFuAhBoECQOVMjDJp53s2xeqaSzHY8nc17tmkLGwfGNl': [0, 9], | ||
SvIzsqYMyQwI3GWgJAe17URxX8V924Co0DaTZLtFjHriEn5bPhcSkfmvOslpBu: [0, 0], | ||
n3qafPOLKdfHpuNw3M61r95svbeJGk7aAEgYn4WlSjXURmF8IDqZBy0CT2VxQc: [0, 1], | ||
tryFJbWcFMiYPg8sASm51uIV93GXTnvRzyfLleh06CpodJD42B7OraKtkQNxUZ: [0, 2], | ||
eg6ql0A3XmvPoCzMlB6DraNGcWSIy5VR8iYup2Qk4tjZFKe1hbwfgHdUTsnLqE: [0, 3], | ||
rSCFlp0rB2inEljaRdxKt7FkIbODSf8wYgTsZM1HL9JzN35cyoqueUvVWCm4hX: [0, 4], | ||
sR8xjC8WQkOwo74PnglH1YFdTI0eaf56RGVSitzbjuZ3shNUXBrqLxEJyAmKv2: [0, 5], | ||
uY2MYFqCLpgx5XQcjdtZK286AwWV7IBGEfuS9yTmbJvkzoUPeYRHr4iDs3naN0: [0, 6], | ||
'74dID7X28VLQhBlnGmjZrec5wTA1fqpWtK4YkaoEIM9SRNiC3gUJH0OFvsPDdy': [0, 7], | ||
'30WXpesPhgKiEI5RHTY7xbB1GnytJvXOl2p0AcUjdF6waZDo9Qk8VLzMuWrqCS': [0, 8], | ||
moxr3HqLAK0GsTND6jowfZz3SUx7cQ8aC54Pl1RbIvFXmEJuBMYVeW9yrdOtin: [0, 9], | ||
} | ||
@@ -44,7 +77,5 @@ | ||
it('min lengths', () => { | ||
const s = new Sqids() | ||
for (const minLength of [0, 1, 5, 10, defaultOptions.alphabet.length]) { | ||
for (const numbers of [ | ||
[s.minValue()], | ||
[0], | ||
[0, 0, 0, 0, 0], | ||
@@ -55,3 +86,3 @@ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], | ||
[1_000_000], | ||
[s.maxValue()], | ||
[Number.MAX_SAFE_INTEGER], | ||
]) { | ||
@@ -70,6 +101,4 @@ const sqids = new Sqids({ | ||
it('out-of-range invalid min length', () => { | ||
const s = new Sqids() | ||
const err = `Minimum length has to be between ${s.minValue()} and ${ | ||
defaultOptions.alphabet.length | ||
}` | ||
const minLengthLimit = 255 | ||
const minLengthError = `Minimum length has to be between 0 and ${minLengthLimit}` | ||
@@ -81,3 +110,3 @@ expect( | ||
}), | ||
).toThrow(err) | ||
).toThrow(minLengthError) | ||
@@ -87,6 +116,6 @@ expect( | ||
new Sqids({ | ||
minLength: defaultOptions.alphabet.length + 1, | ||
minLength: minLengthLimit + 1, | ||
}), | ||
).toThrow(err) | ||
).toThrow(minLengthError) | ||
}) | ||
}) |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
0
127872
2683