From 0fbd813fceaddd14e1e34cb24f811046c4787cba Mon Sep 17 00:00:00 2001 From: Matheus Xavier Date: Wed, 16 Sep 2020 07:28:58 -0300 Subject: [PATCH] add scripts and improve the api a bit --- Path.ts | 135 +++++++++++++++-- README.md | 8 +- example.ts | 5 + hashids.ts | 412 +++++++++++++++++++++++++++++++++++++++++++++++++++ mod.js | 2 + mod.js.map | 1 + mod.ts | 3 +- scripts.yaml | 2 + test.ts | 52 +++++-- 9 files changed, 594 insertions(+), 26 deletions(-) create mode 100644 example.ts create mode 100644 hashids.ts create mode 100644 mod.js create mode 100644 mod.js.map create mode 100644 scripts.yaml diff --git a/Path.ts b/Path.ts index 0fb73f4..a3529c4 100644 --- a/Path.ts +++ b/Path.ts @@ -1,5 +1,6 @@ // Copryright 2020 Matheus Xavier all rights reserved. MIT licensed import { _determineSeparators } from "./_separator.ts"; +import Hashids from "./hashids.ts"; export const LINUX_SEPS = ["/"]; export const WINDOWS_SEPS = ["\\", "/"]; @@ -13,9 +14,9 @@ export class Path { public trailingSlash: boolean = false; /** - * - * @param path initialiize this instance with a path if passed - * @param separators not needed most of the time + * Construct a path object + * @param path initialize this instance with a path if passed + * @param separators not needed most of the time allows for */ constructor(path?: string, separators?: string[]) { this.separators = separators || _determineSeparators(); @@ -56,19 +57,64 @@ export class Path { /** * @returns the stored path structure as a string - * the preferred system separator will be used + * using the preferred system separator. */ - public toString(): string { - const path = this.pathElements.join(this.separators[0]) + public toString(prefix?: string, suffix?: string): string { + let path = this.pathElements.join(this.separators[0]); + prefix = prefix || ""; + suffix = suffix || ""; + path = prefix.concat(path.concat(suffix)); return this.trailingSlash ? "/".concat(path) : path; } - public push(e: string) { - this.pathElements.push(e); + /** + * push a path fragment onto the end of this Path + * @param e a string denoting a Path fragment + */ + public push(e: string): Path { + let pe = Path.explodePath(this.separatorList, e); + pe.forEach((e) => this.pathElements.push(e)); + return this; } - public pop(): string | undefined { - return this.pathElements.pop(); + public pop(): Path { + this.pathElements.pop(); + return this; + } + + /** + * finds the first valid node walking a path from the right + * @param ignoreFiles if set files will be ignored on the resolution + * @returns a new Path object with a Path object until the valid node + */ + public findLastValidNode(ignoreFiles?: boolean): Path { + let strRepr = this.toString(); + const np = new Path(strRepr); + if (ignoreFiles) { + while (!np.exists && !np.isFile) { + np.pop(); + } + } else { + while (!np.exists) { + np.pop(); + } + } + return np; + } + + /** + * takes the diff between Path x and Path y + * @param x + * @param y + * @returns elements in x but not in y + */ + public static diff(x: Path, y: Path): string[] { + const xRepr = x.elements; + const yRepr = y.elements; + let res = xRepr.filter((e) => { + return yRepr.indexOf(e) === -1; + }); + return res; } /** @@ -143,7 +189,7 @@ export class Path { } /** - * rquest the inner representation of the path inside the class + * request the inner representation of the path inside the class */ get elements(): string[] { return this.pathElements; @@ -163,4 +209,71 @@ export class Path { get separatorList(): string[] { return this.separators; } + + public static fromCWD(): Path { + return new Path(Deno.cwd()); + } +} + +/** + * + * @param path the desired path + * @param parents whether or not to create the structure needed to achieve the final path + * @returns `true` on success and `false` on failure + */ +export function mkDirSync(path: Path, parents: boolean): boolean { + // if the path already exists and is a dir there is nothing to do + if (path.exists && path.isDir) { + return true; + } + // find the last part of the path that is valid + let vp = path.findLastValidNode(); + // take the diff between the valid path and the desired path + let needs = Path.diff(path, vp); + // create the needed paths + for (let i = 0; i < needs.length; i++) { + vp.push(needs[i]); + Deno.mkdirSync(vp.toString()); + } + return true; +} + +export async function mkDir(path: Path, parents: boolean): Promise { + // if the path already exists and is a dir there is nothing to do + if (path.exists && path.isDir) { + return true; + } + // find the last part of the path that is valid + let vp = path.findLastValidNode(); + // take the diff between the valid path and the desired path + let needs = Path.diff(path, vp); + // create the needed paths + for (let i = 0; i < needs.length; i++) { + vp.push(needs[i]); + await Deno.mkdir(vp.toString()); + } + return true; +} + +export function genTmpPath( + rngScalar: number = 4096, + prefix: string = "", + suffix: string = "", + joinChar: string = ".", +): Path { + const rn = Math.floor(Math.random() * rngScalar); + const hsi = new Hashids(rn.toString(), 10); + let tempPath; + prefix = prefix ? prefix+joinChar:""; + suffix = suffix ? joinChar+suffix:""; + switch (Deno.build.os) { + case "windows": + tempPath = Deno.env.get("TMP") || Deno.env.get("TEMP"); + break; + case "darwin": + case "linux": + tempPath = Deno.env.get("TMPDIR") || "/tmp"; + } + let pt = new Path(tempPath) + return pt.push(prefix + hsi.encode(rn) + suffix); } diff --git a/README.md b/README.md index 777305d..a76c312 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,12 @@ console.log(nixPath.exists); ``` # Features -* Handles windows acceptance of `\\` or `/` as separators -* On linux `\\` is treated as escaped charachters correctly +* Handles windows acceptance of `\` or `/` as separators +* On linux `\` is treated as escaped charachters correctly * Easily manipulate paths by pushing/poping or like an array * Get file extensions with ease and correctly * Make assertions about a path + +# Thanks +this lib incorporates work from the hashids lib found on hashids.ts +Copyright (c) 2012-2020 Bazyli Brzóska & Ivan Akimov diff --git a/example.ts b/example.ts new file mode 100644 index 0000000..1306bc1 --- /dev/null +++ b/example.ts @@ -0,0 +1,5 @@ +import Path from "./mod.ts"; +import { genTmpPath } from "./mod.js"; + +const path = genTmpPath(); +console.log(path.toString()); diff --git a/hashids.ts b/hashids.ts new file mode 100644 index 0000000..41e0c63 --- /dev/null +++ b/hashids.ts @@ -0,0 +1,412 @@ +// Copyright (c) 2012-2020 Bazyli Brzóska & Ivan Akimov + +type NumberLike = number | bigint + +export default class Hashids { + private alphabet: string[] + private seps: string[] + private guards: string[] + private salt: string[] + private guardsRegExp: RegExp + private sepsRegExp: RegExp + private allowedCharsRegExp: RegExp + + public constructor( + salt = '', + private minLength = 0, + alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', + seps = 'cfhistuCFHISTU', + ) { + if (typeof minLength !== 'number') { + throw new TypeError( + `Hashids: Provided 'minLength' has to be a number (is ${typeof minLength})`, + ) + } + if (typeof salt !== 'string') { + throw new TypeError( + `Hashids: Provided 'salt' has to be a string (is ${typeof salt})`, + ) + } + if (typeof alphabet !== 'string') { + throw new TypeError( + `Hashids: Provided alphabet has to be a string (is ${typeof alphabet})`, + ) + } + + const saltChars = Array.from(salt) + const alphabetChars = Array.from(alphabet) + const sepsChars = Array.from(seps) + + this.salt = saltChars + + const uniqueAlphabet = keepUnique(alphabetChars) + + if (uniqueAlphabet.length < minAlphabetLength) { + throw new Error( + `Hashids: alphabet must contain at least ${minAlphabetLength} unique characters, provided: ${uniqueAlphabet}`, + ) + } + + /** `alphabet` should not contains `seps` */ + this.alphabet = withoutChars(uniqueAlphabet, sepsChars) + /** `seps` should contain only characters present in `alphabet` */ + const filteredSeps = onlyChars(sepsChars, uniqueAlphabet) + this.seps = shuffle(filteredSeps, saltChars) + + let sepsLength + let diff + + if ( + this.seps.length === 0 || + this.alphabet.length / this.seps.length > sepDiv + ) { + sepsLength = Math.ceil(this.alphabet.length / sepDiv) + + if (sepsLength > this.seps.length) { + diff = sepsLength - this.seps.length + this.seps.push(...this.alphabet.slice(0, diff)) + this.alphabet = this.alphabet.slice(diff) + } + } + + this.alphabet = shuffle(this.alphabet, saltChars) + const guardCount = Math.ceil(this.alphabet.length / guardDiv) + + if (this.alphabet.length < 3) { + this.guards = this.seps.slice(0, guardCount) + this.seps = this.seps.slice(guardCount) + } else { + this.guards = this.alphabet.slice(0, guardCount) + this.alphabet = this.alphabet.slice(guardCount) + } + + this.guardsRegExp = makeAnyOfCharsRegExp(this.guards) + this.sepsRegExp = makeAnyOfCharsRegExp(this.seps) + this.allowedCharsRegExp = makeAtLeastSomeCharRegExp([ + ...this.alphabet, + ...this.guards, + ...this.seps, + ]) + } + + public encode(numbers: string): string + public encode(numbers: NumberLike[]): string + public encode(...numbers: NumberLike[]): string + public encode(numbers: string[]): string + public encode(...numbers: string[]): string + public encode( + first: T[] | T, + ...numbers: T[] + ): string { + const ret = '' + + if (Array.isArray(first)) { + numbers = first + } else { + // eslint-disable-next-line eqeqeq + numbers = [...(first != null ? [first] : []), ...numbers] + } + + if (!numbers.length) { + return ret + } + + if (!numbers.every(isIntegerNumber)) { + numbers = numbers.map((n) => + typeof n === 'bigint' || typeof n === 'number' + ? n + : safeParseInt10(String(n)), + ) as T[] + } + + if (!(numbers as NumberLike[]).every(isPositiveAndFinite)) { + return ret + } + + return this._encode(numbers as number[]).join('') + } + + public decode(id: string): NumberLike[] { + if (!id || typeof id !== 'string' || id.length === 0) return [] + return this._decode(id) + } + + /** + * @description Splits a hex string into groups of 12-digit hexadecimal numbers, + * then prefixes each with '1' and encodes the resulting array of numbers + * + * Encoding '00000000000f00000000000f000f' would be the equivalent of: + * Hashids.encode([0x100000000000f, 0x100000000000f, 0x1000f]) + * + * This means that if your environment supports BigInts, + * you will get different (shorter) results if you provide + * a BigInt representation of your hex and use `encode` directly, e.g.: + * Hashids.encode(BigInt(`0x${hex}`)) + * + * To decode such a representation back to a hex string, use the following snippet: + * Hashids.decode(id)[0].toString(16) + */ + public encodeHex(hex: string | bigint): string { + switch (typeof hex) { + case 'bigint': + hex = hex.toString(16) + break + case 'string': + if (!/^[0-9a-fA-F]+$/.test(hex)) return '' + break + default: + throw new Error( + `Hashids: The provided value is neither a string, nor a BigInt (got: ${typeof hex})`, + ) + } + + const numbers = splitAtIntervalAndMap(hex, 12, (part) => + parseInt(`1${part}`, 16), + ) + return this.encode(numbers) + } + + public decodeHex(id: string) { + return this.decode(id) + .map((number) => number.toString(16).slice(1)) + .join('') + } + + private _encode(numbers: NumberLike[]): string[] { + let alphabet = this.alphabet + + const numbersIdInt = numbers.reduce( + (last, number, i) => + last + + (typeof number === 'bigint' + ? Number(number % BigInt(i + 100)) + : number % (i + 100)), + 0, + ) + + let ret: string[] = [alphabet[numbersIdInt % alphabet.length]] + const lottery = ret.slice() + + const seps = this.seps + const guards = this.guards + + numbers.forEach((number, i) => { + const buffer = lottery.concat(this.salt, alphabet) + + alphabet = shuffle(alphabet, buffer) + const last = toAlphabet(number, alphabet) + + ret.push(...last) + + if (i + 1 < numbers.length) { + const charCode = last[0].codePointAt(0)! + i + const extraNumber = + typeof number === 'bigint' + ? Number(number % BigInt(charCode)) + : number % charCode + ret.push(seps[extraNumber % seps.length]) + } + }) + + if (ret.length < this.minLength) { + const prefixGuardIndex = + (numbersIdInt + ret[0].codePointAt(0)!) % guards.length + ret.unshift(guards[prefixGuardIndex]) + + if (ret.length < this.minLength) { + const suffixGuardIndex = + (numbersIdInt + ret[2].codePointAt(0)!) % guards.length + ret.push(guards[suffixGuardIndex]) + } + } + + const halfLength = Math.floor(alphabet.length / 2) + while (ret.length < this.minLength) { + alphabet = shuffle(alphabet, alphabet) + ret.unshift(...alphabet.slice(halfLength)) + ret.push(...alphabet.slice(0, halfLength)) + + const excess = ret.length - this.minLength + if (excess > 0) { + const halfOfExcess = excess / 2 + ret = ret.slice(halfOfExcess, halfOfExcess + this.minLength) + } + } + + return ret + } + + public isValidId(id: string): boolean { + return this.allowedCharsRegExp.test(id) + } + + private _decode(id: string): NumberLike[] { + if (!this.isValidId(id)) { + throw new Error( + `The provided ID (${id}) is invalid, as it contains characters that do not exist in the alphabet (${this.guards.join( + '', + )}${this.seps.join('')}${this.alphabet.join('')})`, + ) + } + const idGuardsArray = id.split(this.guardsRegExp) + const splitIndex = + idGuardsArray.length === 3 || idGuardsArray.length === 2 ? 1 : 0 + + const idBreakdown = idGuardsArray[splitIndex] + if (idBreakdown.length === 0) return [] + + const lotteryChar = idBreakdown[Symbol.iterator]().next().value as string + const idArray = idBreakdown.slice(lotteryChar.length).split(this.sepsRegExp) + + let lastAlphabet: string[] = this.alphabet + const result: NumberLike[] = [] + + for (const subId of idArray) { + const buffer = [lotteryChar, ...this.salt, ...lastAlphabet] + const nextAlphabet = shuffle( + lastAlphabet, + buffer.slice(0, lastAlphabet.length), + ) + result.push(fromAlphabet(Array.from(subId), nextAlphabet)) + lastAlphabet = nextAlphabet + } + + // if the result is different from what we'd expect, we return an empty result (malformed input): + if (this._encode(result).join('') !== id) return [] + return result + } +} + +const minAlphabetLength = 16 +const sepDiv = 3.5 +const guardDiv = 12 + +export const keepUnique = (content: Iterable): T[] => + Array.from(new Set(content)) + +export const withoutChars = ( + chars: string[], + withoutChars: string[], +): string[] => chars.filter((char) => !withoutChars.includes(char)) + +export const onlyChars = (chars: string[], keepChars: string[]): string[] => + chars.filter((char) => keepChars.includes(char)) + +const isIntegerNumber = (n: NumberLike | string) => + typeof n === 'bigint' || + (!Number.isNaN(Number(n)) && Math.floor(Number(n)) === n) + +const isPositiveAndFinite = (n: NumberLike) => + typeof n === 'bigint' || (n >= 0 && Number.isSafeInteger(n)) + +function shuffle(alphabetChars: string[], saltChars: string[]): string[] { + if (saltChars.length === 0) { + return alphabetChars + } + + let integer: number + const transformed = alphabetChars.slice() + + for (let i = transformed.length - 1, v = 0, p = 0; i > 0; i--, v++) { + v %= saltChars.length + p += integer = saltChars[v].codePointAt(0)! + const j = (integer + v + p) % i + + // swap characters at positions i and j + const a = transformed[i] + const b = transformed[j] + transformed[j] = a + transformed[i] = b + } + + return transformed +} + +const toAlphabet = (input: NumberLike, alphabetChars: string[]): string[] => { + const id: string[] = [] + + if (typeof input === 'bigint') { + const alphabetLength = BigInt(alphabetChars.length) + do { + id.unshift(alphabetChars[Number(input % alphabetLength)]) + input = input / alphabetLength + } while (input > BigInt(0)) + } else { + do { + id.unshift(alphabetChars[input % alphabetChars.length]) + input = Math.floor(input / alphabetChars.length) + } while (input > 0) + } + + return id +} + +const fromAlphabet = ( + inputChars: string[], + alphabetChars: string[], +): NumberLike => + inputChars.reduce((carry, item) => { + const index = alphabetChars.indexOf(item) + if (index === -1) { + throw new Error( + `The provided ID (${inputChars.join( + '', + )}) is invalid, as it contains characters that do not exist in the alphabet (${alphabetChars.join( + '', + )})`, + ) + } + if (typeof carry === 'bigint') { + return carry * BigInt(alphabetChars.length) + BigInt(index) + } + const value = carry * alphabetChars.length + index + const isSafeValue = Number.isSafeInteger(value) + if (isSafeValue) { + return value + } else { + if (typeof BigInt === 'function') { + return BigInt(carry) * BigInt(alphabetChars.length) + BigInt(index) + } else { + // we do not have support for BigInt: + throw new Error( + `Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment`, + ) + } + } + }, 0 as NumberLike) + +const safeToParseNumberRegExp = /^\+?[0-9]+$/ +const safeParseInt10 = (str: string) => + safeToParseNumberRegExp.test(str) ? parseInt(str, 10) : NaN + +const splitAtIntervalAndMap = ( + str: string, + nth: number, + map: (n: string) => T, +): T[] => + Array.from({length: Math.ceil(str.length / nth)}, (_, index) => + map(str.slice(index * nth, (index + 1) * nth)), + ) + +const makeAnyOfCharsRegExp = (chars: string[]) => + new RegExp( + chars + .map((char) => escapeRegExp(char)) + // we need to sort these from longest to shortest, + // as they may contain multibyte unicode characters (these should come first) + .sort((a, b) => b.length - a.length) + .join('|'), + ) + +const makeAtLeastSomeCharRegExp = (chars: string[]) => + new RegExp( + `^[${chars + .map((char) => escapeRegExp(char)) + // we need to sort these from longest to shortest, + // as they may contain multibyte unicode characters (these should come first) + .sort((a, b) => b.length - a.length) + .join('')}]+$`, + ) + +const escapeRegExp = (text: string) => + text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') diff --git a/mod.js b/mod.js new file mode 100644 index 0000000..1921a30 --- /dev/null +++ b/mod.js @@ -0,0 +1,2 @@ +class t{constructor(t="",h=0,a="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",c="cfhistuCFHISTU"){if(this.minLength=h,"number"!=typeof h)throw new TypeError(`Hashids: Provided 'minLength' has to be a number (is ${typeof h})`);if("string"!=typeof t)throw new TypeError(`Hashids: Provided 'salt' has to be a string (is ${typeof t})`);if("string"!=typeof a)throw new TypeError(`Hashids: Provided alphabet has to be a string (is ${typeof a})`);const p=Array.from(t),g=Array.from(a),u=Array.from(c);this.salt=p;const d=i(g);if(d.lengths)&&(y=Math.ceil(this.alphabet.length/s),y>this.seps.length&&(w=y-this.seps.length,this.seps.push(...this.alphabet.slice(0,w)),this.alphabet=this.alphabet.slice(w))),this.alphabet=l(this.alphabet,p);const x=Math.ceil(this.alphabet.length/n);this.alphabet.length<3?(this.guards=this.seps.slice(0,x),this.seps=this.seps.slice(x)):(this.guards=this.alphabet.slice(0,x),this.alphabet=this.alphabet.slice(x)),this.guardsRegExp=f(this.guards),this.sepsRegExp=f(this.seps),this.allowedCharsRegExp=m([...this.alphabet,...this.guards,...this.seps])}encode(t,...e){return(e=Array.isArray(t)?t:[...null!=t?[t]:[],...e]).length?(e.every(h)||(e=e.map(t=>"bigint"==typeof t||"number"==typeof t?t:u(String(t)))),e.every(a)?this._encode(e).join(""):""):""}decode(t){return t&&"string"==typeof t&&0!==t.length?this._decode(t):[]}encodeHex(t){switch(typeof t){case"bigint":t=t.toString(16);break;case"string":if(!/^[0-9a-fA-F]+$/.test(t))return"";break;default:throw new Error(`Hashids: The provided value is neither a string, nor a BigInt (got: ${typeof t})`)}const e=d(t,12,t=>parseInt("1"+t,16));return this.encode(e)}decodeHex(t){return this.decode(t).map(t=>t.toString(16).slice(1)).join("")}_encode(t){let e=this.alphabet;const s=t.reduce((t,e,s)=>t+("bigint"==typeof e?Number(e%BigInt(s+100)):e%(s+100)),0);let n=[e[s%e.length]];const i=n.slice(),r=this.seps,o=this.guards;if(t.forEach((s,o)=>{const h=i.concat(this.salt,e);e=l(e,h);const a=c(s,e);if(n.push(...a),o+10){const e=t/2;n=n.slice(e,e+this.minLength)}}return n}isValidId(t){return this.allowedCharsRegExp.test(t)}_decode(t){if(!this.isValidId(t))throw new Error(`The provided ID (${t}) is invalid, as it contains characters that do not exist in the alphabet (${this.guards.join("")}${this.seps.join("")}${this.alphabet.join("")})`);const e=t.split(this.guardsRegExp),s=e[3===e.length||2===e.length?1:0];if(0===s.length)return[];const n=s[Symbol.iterator]().next().value,i=s.slice(n.length).split(this.sepsRegExp);let r=this.alphabet;const o=[];for(const t of i){const e=l(r,[n,...this.salt,...r].slice(0,r.length));o.push(p(Array.from(t),e)),r=e}return this._encode(o).join("")!==t?[]:o}}const e=16,s=3.5,n=12,i=t=>Array.from(new Set(t)),r=(t,e)=>t.filter(t=>!e.includes(t)),o=(t,e)=>t.filter(t=>e.includes(t)),h=t=>"bigint"==typeof t||!Number.isNaN(Number(t))&&Math.floor(Number(t))===t,a=t=>"bigint"==typeof t||t>=0&&Number.isSafeInteger(t);function l(t,e){if(0===e.length)return t;let s;const n=t.slice();for(let t=n.length-1,i=0,r=0;t>0;t--,i++){i%=e.length,r+=s=e[i].codePointAt(0);const o=(s+i+r)%t,h=n[t],a=n[o];n[o]=h,n[t]=a}return n}const c=(t,e)=>{const s=[];if("bigint"==typeof t){const n=BigInt(e.length);do{s.unshift(e[Number(t%n)]),t/=n}while(t>BigInt(0))}else do{s.unshift(e[t%e.length]),t=Math.floor(t/e.length)}while(t>0);return s},p=(t,e)=>t.reduce((s,n)=>{const i=e.indexOf(n);if(-1===i)throw new Error(`The provided ID (${t.join("")}) is invalid, as it contains characters that do not exist in the alphabet (${e.join("")})`);if("bigint"==typeof s)return s*BigInt(e.length)+BigInt(i);const r=s*e.length+i;if(Number.isSafeInteger(r))return r;if("function"==typeof BigInt)return BigInt(s)*BigInt(e.length)+BigInt(i);throw new Error("Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment")},0),g=/^\+?[0-9]+$/,u=t=>g.test(t)?parseInt(t,10):NaN,d=(t,e,s)=>Array.from({length:Math.ceil(t.length/e)},(n,i)=>s(t.slice(i*e,(i+1)*e))),f=t=>new RegExp(t.map(t=>b(t)).sort((t,e)=>e.length-t.length).join("|")),m=t=>new RegExp(`^[${t.map(t=>b(t)).sort((t,e)=>e.length-t.length).join("")}]+$`),b=t=>t.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),y=["/"],w=["\\","/"];class x{constructor(t,e){this.trailingSlash=!1,this.separators=e||function(){switch(Deno.build.os){case"linux":case"darwin":return["/"];case"windows":return["\\","/"];default:throw Error("unexpected platform")}}(),t?(this.trailingSlash="/"===t[0],this.pathElements=x.explodePath(this.separators,t)):this.pathElements=new Array}static explodePath(t,e){const s=e.split(""),n=new Array;let i="";for(let e=0;ethis.pathElements.push(t)),this}pop(){return this.pathElements.pop(),this}findLastValidNode(t){let e=this.toString();const s=new x(e);if(t)for(;!s.exists&&!s.isFile;)s.pop();else for(;!s.exists;)s.pop();return s}static diff(t,e){const s=t.elements,n=e.elements;return s.filter(t=>-1===n.indexOf(t))}get ext(){const t=this.pathElements[this.pathElements.length-1],e=t.split("").lastIndexOf(".");return 0!==e&&-1!==e?t.substr(e):null}get exists(){try{return Deno.statSync(this.toString()),!0}catch(t){if(t instanceof Deno.errors.PermissionDenied)throw t;return!1}}get isFile(){try{return Deno.statSync(this.toString()).isFile}catch(t){if(t instanceof Deno.errors.PermissionDenied)throw t;return!1}}get isDir(){try{return Deno.statSync(this.toString()).isDirectory}catch(t){if(t instanceof Deno.errors.PermissionDenied)throw t;return!1}}get isSymlink(){try{return Deno.statSync(this.toString()).isSymlink}catch(t){if(t instanceof Deno.errors.PermissionDenied)throw t;return!1}}get elements(){return this.pathElements}set elements(t){this.pathElements=t}set separatorList(t){this.separators=t}get separatorList(){return this.separators}static fromCWD(){return new x(Deno.cwd())}}function E(t,e){if(t.exists&&t.isDir)return!0;let s=t.findLastValidNode(),n=x.diff(t,s);for(let t=0;t sepDiv\n ) {\n sepsLength = Math.ceil(this.alphabet.length / sepDiv)\n\n if (sepsLength > this.seps.length) {\n diff = sepsLength - this.seps.length\n this.seps.push(...this.alphabet.slice(0, diff))\n this.alphabet = this.alphabet.slice(diff)\n }\n }\n\n this.alphabet = shuffle(this.alphabet, saltChars)\n const guardCount = Math.ceil(this.alphabet.length / guardDiv)\n\n if (this.alphabet.length < 3) {\n this.guards = this.seps.slice(0, guardCount)\n this.seps = this.seps.slice(guardCount)\n } else {\n this.guards = this.alphabet.slice(0, guardCount)\n this.alphabet = this.alphabet.slice(guardCount)\n }\n\n this.guardsRegExp = makeAnyOfCharsRegExp(this.guards)\n this.sepsRegExp = makeAnyOfCharsRegExp(this.seps)\n this.allowedCharsRegExp = makeAtLeastSomeCharRegExp([\n ...this.alphabet,\n ...this.guards,\n ...this.seps,\n ])\n }\n\n public encode(numbers: string): string\n public encode(numbers: NumberLike[]): string\n public encode(...numbers: NumberLike[]): string\n public encode(numbers: string[]): string\n public encode(...numbers: string[]): string\n public encode(\n first: T[] | T,\n ...numbers: T[]\n ): string {\n const ret = ''\n\n if (Array.isArray(first)) {\n numbers = first\n } else {\n // eslint-disable-next-line eqeqeq\n numbers = [...(first != null ? [first] : []), ...numbers]\n }\n\n if (!numbers.length) {\n return ret\n }\n\n if (!numbers.every(isIntegerNumber)) {\n numbers = numbers.map((n) =>\n typeof n === 'bigint' || typeof n === 'number'\n ? n\n : safeParseInt10(String(n)),\n ) as T[]\n }\n\n if (!(numbers as NumberLike[]).every(isPositiveAndFinite)) {\n return ret\n }\n\n return this._encode(numbers as number[]).join('')\n }\n\n public decode(id: string): NumberLike[] {\n if (!id || typeof id !== 'string' || id.length === 0) return []\n return this._decode(id)\n }\n\n /**\n * @description Splits a hex string into groups of 12-digit hexadecimal numbers,\n * then prefixes each with '1' and encodes the resulting array of numbers\n *\n * Encoding '00000000000f00000000000f000f' would be the equivalent of:\n * Hashids.encode([0x100000000000f, 0x100000000000f, 0x1000f])\n *\n * This means that if your environment supports BigInts,\n * you will get different (shorter) results if you provide\n * a BigInt representation of your hex and use `encode` directly, e.g.:\n * Hashids.encode(BigInt(`0x${hex}`))\n *\n * To decode such a representation back to a hex string, use the following snippet:\n * Hashids.decode(id)[0].toString(16)\n */\n public encodeHex(hex: string | bigint): string {\n switch (typeof hex) {\n case 'bigint':\n hex = hex.toString(16)\n break\n case 'string':\n if (!/^[0-9a-fA-F]+$/.test(hex)) return ''\n break\n default:\n throw new Error(\n `Hashids: The provided value is neither a string, nor a BigInt (got: ${typeof hex})`,\n )\n }\n\n const numbers = splitAtIntervalAndMap(hex, 12, (part) =>\n parseInt(`1${part}`, 16),\n )\n return this.encode(numbers)\n }\n\n public decodeHex(id: string) {\n return this.decode(id)\n .map((number) => number.toString(16).slice(1))\n .join('')\n }\n\n private _encode(numbers: NumberLike[]): string[] {\n let alphabet = this.alphabet\n\n const numbersIdInt = numbers.reduce(\n (last, number, i) =>\n last +\n (typeof number === 'bigint'\n ? Number(number % BigInt(i + 100))\n : number % (i + 100)),\n 0,\n )\n\n let ret: string[] = [alphabet[numbersIdInt % alphabet.length]]\n const lottery = ret.slice()\n\n const seps = this.seps\n const guards = this.guards\n\n numbers.forEach((number, i) => {\n const buffer = lottery.concat(this.salt, alphabet)\n\n alphabet = shuffle(alphabet, buffer)\n const last = toAlphabet(number, alphabet)\n\n ret.push(...last)\n\n if (i + 1 < numbers.length) {\n const charCode = last[0].codePointAt(0)! + i\n const extraNumber =\n typeof number === 'bigint'\n ? Number(number % BigInt(charCode))\n : number % charCode\n ret.push(seps[extraNumber % seps.length])\n }\n })\n\n if (ret.length < this.minLength) {\n const prefixGuardIndex =\n (numbersIdInt + ret[0].codePointAt(0)!) % guards.length\n ret.unshift(guards[prefixGuardIndex])\n\n if (ret.length < this.minLength) {\n const suffixGuardIndex =\n (numbersIdInt + ret[2].codePointAt(0)!) % guards.length\n ret.push(guards[suffixGuardIndex])\n }\n }\n\n const halfLength = Math.floor(alphabet.length / 2)\n while (ret.length < this.minLength) {\n alphabet = shuffle(alphabet, alphabet)\n ret.unshift(...alphabet.slice(halfLength))\n ret.push(...alphabet.slice(0, halfLength))\n\n const excess = ret.length - this.minLength\n if (excess > 0) {\n const halfOfExcess = excess / 2\n ret = ret.slice(halfOfExcess, halfOfExcess + this.minLength)\n }\n }\n\n return ret\n }\n\n public isValidId(id: string): boolean {\n return this.allowedCharsRegExp.test(id)\n }\n\n private _decode(id: string): NumberLike[] {\n if (!this.isValidId(id)) {\n throw new Error(\n `The provided ID (${id}) is invalid, as it contains characters that do not exist in the alphabet (${this.guards.join(\n '',\n )}${this.seps.join('')}${this.alphabet.join('')})`,\n )\n }\n const idGuardsArray = id.split(this.guardsRegExp)\n const splitIndex =\n idGuardsArray.length === 3 || idGuardsArray.length === 2 ? 1 : 0\n\n const idBreakdown = idGuardsArray[splitIndex]\n if (idBreakdown.length === 0) return []\n\n const lotteryChar = idBreakdown[Symbol.iterator]().next().value as string\n const idArray = idBreakdown.slice(lotteryChar.length).split(this.sepsRegExp)\n\n let lastAlphabet: string[] = this.alphabet\n const result: NumberLike[] = []\n\n for (const subId of idArray) {\n const buffer = [lotteryChar, ...this.salt, ...lastAlphabet]\n const nextAlphabet = shuffle(\n lastAlphabet,\n buffer.slice(0, lastAlphabet.length),\n )\n result.push(fromAlphabet(Array.from(subId), nextAlphabet))\n lastAlphabet = nextAlphabet\n }\n\n // if the result is different from what we'd expect, we return an empty result (malformed input):\n if (this._encode(result).join('') !== id) return []\n return result\n }\n}\n\nconst minAlphabetLength = 16\nconst sepDiv = 3.5\nconst guardDiv = 12\n\nexport const keepUnique = (content: Iterable): T[] =>\n Array.from(new Set(content))\n\nexport const withoutChars = (\n chars: string[],\n withoutChars: string[],\n): string[] => chars.filter((char) => !withoutChars.includes(char))\n\nexport const onlyChars = (chars: string[], keepChars: string[]): string[] =>\n chars.filter((char) => keepChars.includes(char))\n\nconst isIntegerNumber = (n: NumberLike | string) =>\n typeof n === 'bigint' ||\n (!Number.isNaN(Number(n)) && Math.floor(Number(n)) === n)\n\nconst isPositiveAndFinite = (n: NumberLike) =>\n typeof n === 'bigint' || (n >= 0 && Number.isSafeInteger(n))\n\nfunction shuffle(alphabetChars: string[], saltChars: string[]): string[] {\n if (saltChars.length === 0) {\n return alphabetChars\n }\n\n let integer: number\n const transformed = alphabetChars.slice()\n\n for (let i = transformed.length - 1, v = 0, p = 0; i > 0; i--, v++) {\n v %= saltChars.length\n p += integer = saltChars[v].codePointAt(0)!\n const j = (integer + v + p) % i\n\n // swap characters at positions i and j\n const a = transformed[i]\n const b = transformed[j]\n transformed[j] = a\n transformed[i] = b\n }\n\n return transformed\n}\n\nconst toAlphabet = (input: NumberLike, alphabetChars: string[]): string[] => {\n const id: string[] = []\n\n if (typeof input === 'bigint') {\n const alphabetLength = BigInt(alphabetChars.length)\n do {\n id.unshift(alphabetChars[Number(input % alphabetLength)])\n input = input / alphabetLength\n } while (input > BigInt(0))\n } else {\n do {\n id.unshift(alphabetChars[input % alphabetChars.length])\n input = Math.floor(input / alphabetChars.length)\n } while (input > 0)\n }\n\n return id\n}\n\nconst fromAlphabet = (\n inputChars: string[],\n alphabetChars: string[],\n): NumberLike =>\n inputChars.reduce((carry, item) => {\n const index = alphabetChars.indexOf(item)\n if (index === -1) {\n throw new Error(\n `The provided ID (${inputChars.join(\n '',\n )}) is invalid, as it contains characters that do not exist in the alphabet (${alphabetChars.join(\n '',\n )})`,\n )\n }\n if (typeof carry === 'bigint') {\n return carry * BigInt(alphabetChars.length) + BigInt(index)\n }\n const value = carry * alphabetChars.length + index\n const isSafeValue = Number.isSafeInteger(value)\n if (isSafeValue) {\n return value\n } else {\n if (typeof BigInt === 'function') {\n return BigInt(carry) * BigInt(alphabetChars.length) + BigInt(index)\n } else {\n // we do not have support for BigInt:\n throw new Error(\n `Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment`,\n )\n }\n }\n }, 0 as NumberLike)\n\nconst safeToParseNumberRegExp = /^\\+?[0-9]+$/\nconst safeParseInt10 = (str: string) =>\n safeToParseNumberRegExp.test(str) ? parseInt(str, 10) : NaN\n\nconst splitAtIntervalAndMap = (\n str: string,\n nth: number,\n map: (n: string) => T,\n): T[] =>\n Array.from({length: Math.ceil(str.length / nth)}, (_, index) =>\n map(str.slice(index * nth, (index + 1) * nth)),\n )\n\nconst makeAnyOfCharsRegExp = (chars: string[]) =>\n new RegExp(\n chars\n .map((char) => escapeRegExp(char))\n // we need to sort these from longest to shortest,\n // as they may contain multibyte unicode characters (these should come first)\n .sort((a, b) => b.length - a.length)\n .join('|'),\n )\n\nconst makeAtLeastSomeCharRegExp = (chars: string[]) =>\n new RegExp(\n `^[${chars\n .map((char) => escapeRegExp(char))\n // we need to sort these from longest to shortest,\n // as they may contain multibyte unicode characters (these should come first)\n .sort((a, b) => b.length - a.length)\n .join('')}]+$`,\n )\n\nconst escapeRegExp = (text: string) =>\n text.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&')\n","// Copryright 2020 Matheus Xavier all rights reserved. MIT licensed\nimport { _determineSeparators } from \"./_separator.ts\";\nimport Hashids from \"./hashids.ts\";\n\nexport const LINUX_SEPS = [\"/\"];\nexport const WINDOWS_SEPS = [\"\\\\\", \"/\"];\n\n/**\n * this class represents a filesystem path, and allows for easy manipulation of said path\n */\nexport class Path {\n private pathElements: string[];\n private separators: string[];\n public trailingSlash: boolean = false;\n\n /**\n * Construct a path object\n * @param path initialize this instance with a path if passed\n * @param separators not needed most of the time allows for \n */\n constructor(path?: string, separators?: string[]) {\n this.separators = separators || _determineSeparators();\n if (path) {\n this.trailingSlash = path[0] === \"/\";\n this.pathElements = Path.explodePath(this.separators, path);\n } else {\n this.pathElements = new Array();\n }\n }\n\n /**\n * explodes a string into an array of strings\n * @param separators a list of valid separators for the host system\n * @param pathString the path to be exploded as a string\n */\n private static explodePath(\n separators: string[],\n pathString: string,\n ): string[] {\n const exploded = pathString.split(\"\");\n const pathElements = new Array();\n let currentElement = \"\";\n for (let charAt = 0; charAt < exploded.length; charAt++) {\n let char = exploded[charAt];\n if (separators.indexOf(char) === -1) {\n currentElement = currentElement + char;\n } else {\n if (currentElement) {\n pathElements.push(currentElement);\n currentElement = \"\";\n }\n }\n }\n pathElements.push(currentElement);\n return pathElements;\n }\n\n /**\n * @returns the stored path structure as a string\n * using the preferred system separator.\n */\n public toString(prefix?: string, suffix?: string): string {\n let path = this.pathElements.join(this.separators[0]);\n prefix = prefix || \"\";\n suffix = suffix || \"\";\n path = prefix.concat(path.concat(suffix));\n return this.trailingSlash ? \"/\".concat(path) : path;\n }\n\n /**\n * push a path fragment onto the end of this Path\n * @param e a string denoting a Path fragment\n */\n public push(e: string): Path {\n let pe = Path.explodePath(this.separatorList, e);\n pe.forEach((e) => this.pathElements.push(e));\n return this;\n }\n\n public pop(): Path {\n this.pathElements.pop();\n return this;\n }\n\n /**\n * finds the first valid node walking a path from the right\n * @param ignoreFiles if set files will be ignored on the resolution\n * @returns a new Path object with a Path object until the valid node\n */\n public findLastValidNode(ignoreFiles?: boolean): Path {\n let strRepr = this.toString();\n const np = new Path(strRepr);\n if (ignoreFiles) {\n while (!np.exists && !np.isFile) {\n np.pop();\n }\n } else {\n while (!np.exists) {\n np.pop();\n }\n }\n return np;\n }\n\n /**\n * takes the diff between Path x and Path y\n * @param x \n * @param y\n * @returns elements in x but not in y\n */\n public static diff(x: Path, y: Path): string[] {\n const xRepr = x.elements;\n const yRepr = y.elements;\n let res = xRepr.filter((e) => {\n return yRepr.indexOf(e) === -1;\n });\n return res;\n }\n\n /**\n * returns the extension or null the dot will not be stripped\n * dotfiles are considered extensionless\n */\n get ext(): string | null {\n const lastElem = this.pathElements[this.pathElements.length - 1];\n const dotIndex = lastElem.split(\"\").lastIndexOf(\".\");\n if (dotIndex !== 0 && dotIndex !== -1) {\n return lastElem.substr(dotIndex);\n } else {\n return null;\n }\n }\n\n /**\n * Checks if the path exists\n * ```ts\n * const path = new Path(\"/home/test/text.txt\");\n * path.exists;\n * ```\n * requires: --allow-read flag\n */\n get exists(): boolean {\n try {\n Deno.statSync(this.toString());\n return true;\n } catch (e) {\n // do not hide permission errors from the user\n if (e instanceof Deno.errors.PermissionDenied) {\n throw e;\n }\n return false;\n }\n }\n\n get isFile(): boolean {\n try {\n return Deno.statSync(this.toString()).isFile;\n } catch (e) {\n // do not hide permission errors from the user\n if (e instanceof Deno.errors.PermissionDenied) {\n throw e;\n }\n return false;\n }\n }\n\n get isDir(): boolean {\n try {\n return Deno.statSync(this.toString()).isDirectory;\n } catch (e) {\n // do not hide permission errors from the user\n if (e instanceof Deno.errors.PermissionDenied) {\n throw e;\n }\n return false;\n }\n }\n\n get isSymlink(): boolean {\n try {\n return Deno.statSync(this.toString()).isSymlink;\n } catch (e) {\n // do not hide permission errors from the user\n if (e instanceof Deno.errors.PermissionDenied) {\n throw e;\n }\n return false;\n }\n }\n\n /**\n * request the inner representation of the path inside the class\n */\n get elements(): string[] {\n return this.pathElements;\n }\n\n /**\n * set the inner representation of the path inside the class\n */\n set elements(e: string[]) {\n this.pathElements = e;\n }\n\n set separatorList(sl: string[]) {\n this.separators = sl;\n }\n\n get separatorList(): string[] {\n return this.separators;\n }\n\n public static fromCWD(): Path {\n return new Path(Deno.cwd());\n }\n}\n\n/**\n * \n * @param path the desired path\n * @param parents whether or not to create the structure needed to achieve the final path\n * @returns `true` on success and `false` on failure\n */\nexport function mkDirSync(path: Path, parents: boolean): boolean {\n // if the path already exists and is a dir there is nothing to do\n if (path.exists && path.isDir) {\n return true;\n }\n // find the last part of the path that is valid\n let vp = path.findLastValidNode();\n // take the diff between the valid path and the desired path\n let needs = Path.diff(path, vp);\n // create the needed paths\n for (let i = 0; i < needs.length; i++) {\n vp.push(needs[i]);\n Deno.mkdirSync(vp.toString());\n }\n return true;\n}\n\nexport async function mkDir(path: Path, parents: boolean): Promise {\n // if the path already exists and is a dir there is nothing to do\n if (path.exists && path.isDir) {\n return true;\n }\n // find the last part of the path that is valid\n let vp = path.findLastValidNode();\n // take the diff between the valid path and the desired path\n let needs = Path.diff(path, vp);\n // create the needed paths\n for (let i = 0; i < needs.length; i++) {\n vp.push(needs[i]);\n await Deno.mkdir(vp.toString());\n }\n return true;\n}\n\nexport function genTmpPath(\n rngScalar: number = 4096,\n prefix: string = \"\",\n suffix: string = \"\",\n joinChar: string = \".\",\n): Path {\n const rn = Math.floor(Math.random() * rngScalar);\n const hsi = new Hashids(rn.toString(), 10);\n let tempPath;\n prefix = prefix ? prefix+joinChar:\"\";\n suffix = suffix ? joinChar+suffix:\"\";\n switch (Deno.build.os) {\n case \"windows\":\n tempPath = Deno.env.get(\"TMP\") || Deno.env.get(\"TEMP\");\n break;\n case \"darwin\":\n case \"linux\":\n tempPath = Deno.env.get(\"TMPDIR\") || \"/tmp\";\n }\n let pt = new Path(tempPath)\n return pt.push(prefix + hsi.encode(rn) + suffix);\n}\n","// Copryright 2020 Matheus Xavier all rights reserved. MIT licensed\n\nexport function _determineSeparators(): string[] {\n switch (Deno.build.os) {\n case \"linux\":\n case \"darwin\":\n return [\"/\"];\n break;\n case \"windows\":\n // windows can have either / or \\ as path separators so we account for that\n // also takes into account window's preference for \\\n return [\"\\\\\", \"/\"];\n break;\n default:\n throw Error(\"unexpected platform\");\n }\n}\n"],"names":["Hashids","[object Object]","salt","minLength","alphabet","seps","this","TypeError","saltChars","Array","from","alphabetChars","sepsChars","uniqueAlphabet","keepUnique","length","minAlphabetLength","Error","withoutChars","filteredSeps","onlyChars","sepsLength","diff","shuffle","sepDiv","Math","ceil","push","slice","guardCount","guardDiv","guards","guardsRegExp","makeAnyOfCharsRegExp","sepsRegExp","allowedCharsRegExp","makeAtLeastSomeCharRegExp","first","numbers","isArray","every","isIntegerNumber","map","n","safeParseInt10","String","isPositiveAndFinite","_encode","join","id","_decode","hex","toString","test","splitAtIntervalAndMap","part","parseInt","encode","decode","number","numbersIdInt","reduce","last","i","Number","BigInt","ret","lottery","forEach","buffer","concat","toAlphabet","charCode","codePointAt","extraNumber","prefixGuardIndex","unshift","suffixGuardIndex","halfLength","floor","excess","halfOfExcess","isValidId","idGuardsArray","split","idBreakdown","lotteryChar","Symbol","iterator","next","value","idArray","lastAlphabet","result","subId","nextAlphabet","fromAlphabet","content","Set","chars","filter","char","includes","keepChars","isNaN","isSafeInteger","integer","transformed","v","p","j","a","b","input","alphabetLength","inputChars","carry","item","index","indexOf","safeToParseNumberRegExp","str","NaN","nth","_","RegExp","escapeRegExp","sort","text","replace","LINUX_SEPS","WINDOWS_SEPS","Path","path","separators","Deno","build","os","_determineSeparators","trailingSlash","pathElements","explodePath","pathString","exploded","currentElement","charAt","prefix","suffix","e","separatorList","pop","ignoreFiles","strRepr","np","exists","isFile","x","y","xRepr","elements","yRepr","ext","lastElem","dotIndex","lastIndexOf","substr","statSync","errors","PermissionDenied","isDir","isDirectory","isSymlink","sl","cwd","mkDirSync","parents","vp","findLastValidNode","needs","mkdirSync","async","mkDir","mkdir","genTmpPath","rngScalar","joinChar","rn","random","hsi","tempPath","env","get"],"mappings":"MAIqBA,EASnBC,YACEC,EAAO,GACCC,EAAY,EACpBC,EAAW,iEACXC,EAAO,kBAEP,GAJQC,eAAAH,EAIiB,iBAAdA,EACT,MAAM,IAAII,UACR,+DAA+DJ,MAGnE,GAAoB,iBAATD,EACT,MAAM,IAAIK,UACR,0DAA0DL,MAG9D,GAAwB,iBAAbE,EACT,MAAM,IAAIG,UACR,4DAA4DH,MAIhE,MAAMI,EAAYC,MAAMC,KAAKR,GACvBS,EAAgBF,MAAMC,KAAKN,GAC3BQ,EAAYH,MAAMC,KAAKL,GAE7BC,KAAKJ,KAAOM,EAEZ,MAAMK,EAAiBC,EAAWH,GAElC,GAAIE,EAAeE,OAASC,EAC1B,MAAM,IAAIC,MACR,2CAA2CD,kCAAkDH,KAKjGP,KAAKF,SAAWc,EAAaL,EAAgBD,GAE7C,MAAMO,EAAeC,EAAUR,EAAWC,GAG1C,IAAIQ,EACAC,EAHJhB,KAAKD,KAAOkB,EAAQJ,EAAcX,IAMX,IAArBF,KAAKD,KAAKU,QACVT,KAAKF,SAASW,OAAST,KAAKD,KAAKU,OAASS,KAE1CH,EAAaI,KAAKC,KAAKpB,KAAKF,SAASW,OAASS,GAE1CH,EAAaf,KAAKD,KAAKU,SACzBO,EAAOD,EAAaf,KAAKD,KAAKU,OAC9BT,KAAKD,KAAKsB,QAAQrB,KAAKF,SAASwB,MAAM,EAAGN,IACzChB,KAAKF,SAAWE,KAAKF,SAASwB,MAAMN,KAIxChB,KAAKF,SAAWmB,EAAQjB,KAAKF,SAAUI,GACvC,MAAMqB,EAAaJ,KAAKC,KAAKpB,KAAKF,SAASW,OAASe,GAEhDxB,KAAKF,SAASW,OAAS,GACzBT,KAAKyB,OAASzB,KAAKD,KAAKuB,MAAM,EAAGC,GACjCvB,KAAKD,KAAOC,KAAKD,KAAKuB,MAAMC,KAE5BvB,KAAKyB,OAASzB,KAAKF,SAASwB,MAAM,EAAGC,GACrCvB,KAAKF,SAAWE,KAAKF,SAASwB,MAAMC,IAGtCvB,KAAK0B,aAAeC,EAAqB3B,KAAKyB,QAC9CzB,KAAK4B,WAAaD,EAAqB3B,KAAKD,MAC5CC,KAAK6B,mBAAqBC,EAA0B,IAC/C9B,KAAKF,YACLE,KAAKyB,UACLzB,KAAKD,OASLJ,OACLoC,KACGC,GAWH,OANEA,EADE7B,MAAM8B,QAAQF,GACNA,EAGA,IAAc,MAATA,EAAgB,CAACA,GAAS,MAAQC,IAGtCvB,QAIRuB,EAAQE,MAAMC,KACjBH,EAAUA,EAAQI,IAAKC,GACR,iBAANA,GAA+B,iBAANA,EAC5BA,EACAC,EAAeC,OAAOF,MAIxBL,EAAyBE,MAAMM,GAI9BxC,KAAKyC,QAAQT,GAAqBU,KAAK,IAzBlC,IAAA,GA4BP/C,OAAOgD,GACZ,OAAKA,GAAoB,iBAAPA,GAAiC,IAAdA,EAAGlC,OACjCT,KAAK4C,QAAQD,GADyC,GAmBxDhD,UAAUkD,GACf,cAAeA,GACb,IAAK,SACHA,EAAMA,EAAIC,SAAS,IACnB,MACF,IAAK,SACH,IAAK,iBAAiBC,KAAKF,GAAM,MAAO,GACxC,MACF,QACE,MAAM,IAAIlC,MACR,8EAA8EkC,MAIpF,MAAMb,EAAUgB,EAAsBH,EAAK,GAAKI,GAC9CC,SAAS,IAAID,EAAQ,KAEvB,OAAOjD,KAAKmD,OAAOnB,GAGdrC,UAAUgD,GACf,OAAO3C,KAAKoD,OAAOT,GAChBP,IAAKiB,GAAWA,EAAOP,SAAS,IAAIxB,MAAM,IAC1CoB,KAAK,IAGF/C,QAAQqC,GACd,IAAIlC,EAAWE,KAAKF,SAEpB,MAAMwD,EAAetB,EAAQuB,OAC3B,CAACC,EAAMH,EAAQI,IACbD,GACmB,iBAAXH,EACJK,OAAOL,EAASM,OAAOF,EAAI,MAC3BJ,GAAUI,EAAI,MACpB,GAGF,IAAIG,EAAgB,CAAC9D,EAASwD,EAAexD,EAASW,SACtD,MAAMoD,EAAUD,EAAItC,QAEdvB,EAAOC,KAAKD,KACZ0B,EAASzB,KAAKyB,OAoBpB,GAlBAO,EAAQ8B,QAAQ,CAACT,EAAQI,KACvB,MAAMM,EAASF,EAAQG,OAAOhE,KAAKJ,KAAME,GAEzCA,EAAWmB,EAAQnB,EAAUiE,GAC7B,MAAMP,EAAOS,EAAWZ,EAAQvD,GAIhC,GAFA8D,EAAIvC,QAAQmC,GAERC,EAAI,EAAIzB,EAAQvB,OAAQ,CAC1B,MAAMyD,EAAWV,EAAK,GAAGW,YAAY,GAAMV,EACrCW,EACc,iBAAXf,EACHK,OAAOL,EAASM,OAAOO,IACvBb,EAASa,EACfN,EAAIvC,KAAKtB,EAAKqE,EAAcrE,EAAKU,YAIjCmD,EAAInD,OAAST,KAAKH,UAAW,CAC/B,MAAMwE,GACHf,EAAeM,EAAI,GAAGO,YAAY,IAAO1C,EAAOhB,OAGnD,GAFAmD,EAAIU,QAAQ7C,EAAO4C,IAEfT,EAAInD,OAAST,KAAKH,UAAW,CAC/B,MAAM0E,GACHjB,EAAeM,EAAI,GAAGO,YAAY,IAAO1C,EAAOhB,OACnDmD,EAAIvC,KAAKI,EAAO8C,KAIpB,MAAMC,EAAarD,KAAKsD,MAAM3E,EAASW,OAAS,GAChD,KAAOmD,EAAInD,OAAST,KAAKH,WAAW,CAClCC,EAAWmB,EAAQnB,EAAUA,GAC7B8D,EAAIU,WAAWxE,EAASwB,MAAMkD,IAC9BZ,EAAIvC,QAAQvB,EAASwB,MAAM,EAAGkD,IAE9B,MAAME,EAASd,EAAInD,OAAST,KAAKH,UACjC,GAAI6E,EAAS,EAAG,CACd,MAAMC,EAAeD,EAAS,EAC9Bd,EAAMA,EAAItC,MAAMqD,EAAcA,EAAe3E,KAAKH,YAItD,OAAO+D,EAGFjE,UAAUgD,GACf,OAAO3C,KAAK6B,mBAAmBkB,KAAKJ,GAG9BhD,QAAQgD,GACd,IAAK3C,KAAK4E,UAAUjC,GAClB,MAAM,IAAIhC,MACR,oBAAoBgC,+EAAgF3C,KAAKyB,OAAOiB,KAC9G,MACE1C,KAAKD,KAAK2C,KAAK,MAAM1C,KAAKF,SAAS4C,KAAK,QAGhD,MAAMmC,EAAgBlC,EAAGmC,MAAM9E,KAAK0B,cAI9BqD,EAAcF,EAFO,IAAzBA,EAAcpE,QAAyC,IAAzBoE,EAAcpE,OAAe,EAAI,GAGjE,GAA2B,IAAvBsE,EAAYtE,OAAc,MAAO,GAErC,MAAMuE,EAAcD,EAAYE,OAAOC,YAAYC,OAAOC,MACpDC,EAAUN,EAAYzD,MAAM0D,EAAYvE,QAAQqE,MAAM9E,KAAK4B,YAEjE,IAAI0D,EAAyBtF,KAAKF,SAClC,MAAMyF,EAAuB,GAE7B,IAAK,MAAMC,KAASH,EAAS,CAC3B,MACMI,EAAexE,EACnBqE,EAFa,CAACN,KAAgBhF,KAAKJ,QAAS0F,GAGrChE,MAAM,EAAGgE,EAAa7E,SAE/B8E,EAAOlE,KAAKqE,EAAavF,MAAMC,KAAKoF,GAAQC,IAC5CH,EAAeG,EAIjB,OAAIzF,KAAKyC,QAAQ8C,GAAQ7C,KAAK,MAAQC,EAAW,GAC1C4C,GAIX,MAAM7E,EAAoB,GACpBQ,EAAS,IACTM,EAAW,GAEJhB,EAAiBmF,GAC5BxF,MAAMC,KAAK,IAAIwF,IAAID,IAER/E,EAAe,CAC1BiF,EACAjF,IACaiF,EAAMC,OAAQC,IAAUnF,EAAaoF,SAASD,IAEhDjF,EAAY,CAAC+E,EAAiBI,IACzCJ,EAAMC,OAAQC,GAASE,EAAUD,SAASD,IAEtC5D,EAAmBE,GACV,iBAANA,IACLqB,OAAOwC,MAAMxC,OAAOrB,KAAOlB,KAAKsD,MAAMf,OAAOrB,MAAQA,EAEnDG,EAAuBH,GACd,iBAANA,GAAmBA,GAAK,GAAKqB,OAAOyC,cAAc9D,GAE3D,SAASpB,EAAQZ,EAAyBH,GACxC,GAAyB,IAArBA,EAAUO,OACZ,OAAOJ,EAGT,IAAI+F,EACJ,MAAMC,EAAchG,EAAciB,QAElC,IAAK,IAAImC,EAAI4C,EAAY5F,OAAS,EAAG6F,EAAI,EAAGC,EAAI,EAAG9C,EAAI,EAAGA,IAAK6C,IAAK,CAClEA,GAAKpG,EAAUO,OACf8F,GAAKH,EAAUlG,EAAUoG,GAAGnC,YAAY,GACxC,MAAMqC,GAAKJ,EAAUE,EAAIC,GAAK9C,EAGxBgD,EAAIJ,EAAY5C,GAChBiD,EAAIL,EAAYG,GACtBH,EAAYG,GAAKC,EACjBJ,EAAY5C,GAAKiD,EAGnB,OAAOL,EAGT,MAAMpC,EAAa,CAAC0C,EAAmBtG,KACrC,MAAMsC,EAAe,GAErB,GAAqB,iBAAVgE,EAAoB,CAC7B,MAAMC,EAAiBjD,OAAOtD,EAAcI,QAC5C,GACEkC,EAAG2B,QAAQjE,EAAcqD,OAAOiD,EAAQC,KACxCD,GAAgBC,QACTD,EAAQhD,OAAO,SAExB,GACEhB,EAAG2B,QAAQjE,EAAcsG,EAAQtG,EAAcI,SAC/CkG,EAAQxF,KAAKsD,MAAMkC,EAAQtG,EAAcI,cAClCkG,EAAQ,GAGnB,OAAOhE,GAGH+C,EAAe,CACnBmB,EACAxG,IAEAwG,EAAWtD,OAAO,CAACuD,EAAOC,KACxB,MAAMC,EAAQ3G,EAAc4G,QAAQF,GACpC,IAAe,IAAXC,EACF,MAAM,IAAIrG,MACR,oBAAoBkG,EAAWnE,KAC7B,iFAC6ErC,EAAcqC,KAC3F,QAIN,GAAqB,iBAAVoE,EACT,OAAOA,EAAQnD,OAAOtD,EAAcI,QAAUkD,OAAOqD,GAEvD,MAAM5B,EAAQ0B,EAAQzG,EAAcI,OAASuG,EAE7C,GADoBtD,OAAOyC,cAAcf,GAEvC,OAAOA,EAEP,GAAsB,mBAAXzB,OACT,OAAOA,OAAOmD,GAASnD,OAAOtD,EAAcI,QAAUkD,OAAOqD,GAG7D,MAAM,IAAIrG,MACR,+GAIL,GAECuG,EAA0B,cAC1B5E,EAAkB6E,GACtBD,EAAwBnE,KAAKoE,GAAOjE,SAASiE,EAAK,IAAMC,IAEpDpE,EAAwB,CAC5BmE,EACAE,EACAjF,IAEAjC,MAAMC,KAAe,CAACK,OAAQU,KAAKC,KAAK+F,EAAI1G,OAAS4G,IAAO,CAACC,EAAGN,IAC9D5E,EAAI+E,EAAI7F,MAAM0F,EAAQK,GAAML,EAAQ,GAAKK,KAGvC1F,EAAwBkE,GAC5B,IAAI0B,OACF1B,EACGzD,IAAK2D,GAASyB,EAAazB,IAG3B0B,KAAK,CAAChB,EAAGC,IAAMA,EAAEjG,OAASgG,EAAEhG,QAC5BiC,KAAK,MAGNZ,EAA6B+D,GACjC,IAAI0B,OACF,KAAK1B,EACFzD,IAAK2D,GAASyB,EAAazB,IAG3B0B,KAAK,CAAChB,EAAGC,IAAMA,EAAEjG,OAASgG,EAAEhG,QAC5BiC,KAAK,UAGN8E,EAAgBE,GACpBA,EAAKC,QAAQ,2BAA4B,QCvZ9BC,EAAa,CAAC,KACdC,EAAe,CAAC,KAAM,WAKtBC,EAUXnI,YAAYoI,EAAeC,GAPpBhI,oBAAyB,EAQ9BA,KAAKgI,WAAaA,cClBpB,OAAQC,KAAKC,MAAMC,IACjB,IAAK,QACL,IAAK,SACH,MAAO,CAAC,KAEV,IAAK,UAGH,MAAO,CAAC,KAAM,KAEhB,QACE,MAAMxH,MAAM,wBDOkByH,GAC5BL,GACF/H,KAAKqI,cAA4B,MAAZN,EAAK,GAC1B/H,KAAKsI,aAAeR,EAAKS,YAAYvI,KAAKgI,WAAYD,IAEtD/H,KAAKsI,aAAe,IAAInI,MASpBR,mBACNqI,EACAQ,GAEA,MAAMC,EAAWD,EAAW1D,MAAM,IAC5BwD,EAAe,IAAInI,MACzB,IAAIuI,EAAiB,GACrB,IAAK,IAAIC,EAAS,EAAGA,EAASF,EAAShI,OAAQkI,IAAU,CACvD,IAAI5C,EAAO0C,EAASE,IACc,IAA9BX,EAAWf,QAAQlB,GACrB2C,GAAkC3C,EAE9B2C,IACFJ,EAAajH,KAAKqH,GAClBA,EAAiB,IAKvB,OADAJ,EAAajH,KAAKqH,GACXJ,EAOF3I,SAASiJ,EAAiBC,GAC/B,IAAId,EAAO/H,KAAKsI,aAAa5F,KAAK1C,KAAKgI,WAAW,IAIlD,OAFAa,EAASA,GAAU,GACnBd,GAFAa,EAASA,GAAU,IAEL5E,OAAO+D,EAAK/D,OAAO6E,IAC1B7I,KAAKqI,cAAgB,IAAIrE,OAAO+D,GAAQA,EAO1CpI,KAAKmJ,GAGV,OAFShB,EAAKS,YAAYvI,KAAK+I,cAAeD,GAC3ChF,QAASgF,GAAM9I,KAAKsI,aAAajH,KAAKyH,IAClC9I,KAGFL,MAEL,OADAK,KAAKsI,aAAaU,MACXhJ,KAQFL,kBAAkBsJ,GACvB,IAAIC,EAAUlJ,KAAK8C,WACnB,MAAMqG,EAAK,IAAIrB,EAAKoB,GACpB,GAAID,EACF,MAAQE,EAAGC,SAAWD,EAAGE,QACvBF,EAAGH,WAGL,MAAQG,EAAGC,QACTD,EAAGH,MAGP,OAAOG,EASFxJ,YAAY2J,EAASC,GAC1B,MAAMC,EAAQF,EAAEG,SACVC,EAAQH,EAAEE,SAIhB,OAHUD,EAAM1D,OAAQgD,IACO,IAAtBY,EAAMzC,QAAQ6B,IASzBa,UACE,MAAMC,EAAW5J,KAAKsI,aAAatI,KAAKsI,aAAa7H,OAAS,GACxDoJ,EAAWD,EAAS9E,MAAM,IAAIgF,YAAY,KAChD,OAAiB,IAAbD,IAAgC,IAAdA,EACbD,EAASG,OAAOF,GAEhB,KAYXT,aACE,IAEE,OADAnB,KAAK+B,SAAShK,KAAK8C,aACZ,EACP,MAAOgG,GAEP,GAAIA,aAAab,KAAKgC,OAAOC,iBAC3B,MAAMpB,EAER,OAAO,GAIXO,aACE,IACE,OAAOpB,KAAK+B,SAAShK,KAAK8C,YAAYuG,OACtC,MAAOP,GAEP,GAAIA,aAAab,KAAKgC,OAAOC,iBAC3B,MAAMpB,EAER,OAAO,GAIXqB,YACE,IACE,OAAOlC,KAAK+B,SAAShK,KAAK8C,YAAYsH,YACtC,MAAOtB,GAEP,GAAIA,aAAab,KAAKgC,OAAOC,iBAC3B,MAAMpB,EAER,OAAO,GAIXuB,gBACE,IACE,OAAOpC,KAAK+B,SAAShK,KAAK8C,YAAYuH,UACtC,MAAOvB,GAEP,GAAIA,aAAab,KAAKgC,OAAOC,iBAC3B,MAAMpB,EAER,OAAO,GAOXW,eACE,OAAOzJ,KAAKsI,aAMdmB,aAAaX,GACX9I,KAAKsI,aAAeQ,EAGtBC,kBAAkBuB,GAChBtK,KAAKgI,WAAasC,EAGpBvB,oBACE,OAAO/I,KAAKgI,WAGPrI,iBACL,OAAO,IAAImI,EAAKG,KAAKsC,iBAUTC,EAAUzC,EAAY0C,GAEpC,GAAI1C,EAAKqB,QAAUrB,EAAKoC,MACtB,OAAO,EAGT,IAAIO,EAAK3C,EAAK4C,oBAEVC,EAAQ9C,EAAK9G,KAAK+G,EAAM2C,GAE5B,IAAK,IAAIjH,EAAI,EAAGA,EAAImH,EAAMnK,OAAQgD,IAChCiH,EAAGrJ,KAAKuJ,EAAMnH,IACdwE,KAAK4C,UAAUH,EAAG5H,YAEpB,OAAO,EAGFgI,eAAeC,EAAMhD,EAAY0C,GAEtC,GAAI1C,EAAKqB,QAAUrB,EAAKoC,MACtB,OAAO,EAGT,IAAIO,EAAK3C,EAAK4C,oBAEVC,EAAQ9C,EAAK9G,KAAK+G,EAAM2C,GAE5B,IAAK,IAAIjH,EAAI,EAAGA,EAAImH,EAAMnK,OAAQgD,IAChCiH,EAAGrJ,KAAKuJ,EAAMnH,UACRwE,KAAK+C,MAAMN,EAAG5H,YAEtB,OAAO,WAGOmI,EACdC,EAAoB,KACpBtC,EAAiB,GACjBC,EAAiB,GACjBsC,EAAmB,KAEnB,MAAMC,EAAKjK,KAAKsD,MAAMtD,KAAKkK,SAAWH,GAChCI,EAAM,IAAI5L,EAAQ0L,EAAGtI,WAAY,IACvC,IAAIyI,EAGJ,OAFA3C,EAASA,EAASA,EAAOuC,EAAS,GAClCtC,EAASA,EAASsC,EAAStC,EAAO,GAC1BZ,KAAKC,MAAMC,IACjB,IAAK,UACHoD,EAAWtD,KAAKuD,IAAIC,IAAI,QAAUxD,KAAKuD,IAAIC,IAAI,QAC/C,MACF,IAAK,SACL,IAAK,QACHF,EAAWtD,KAAKuD,IAAIC,IAAI,WAAa,OAGzC,OADS,IAAI3D,EAAKyD,GACRlK,KAAKuH,EAAS0C,EAAInI,OAAOiI,GAAMvC"} \ No newline at end of file diff --git a/mod.ts b/mod.ts index 392f494..9871cf3 100644 --- a/mod.ts +++ b/mod.ts @@ -1,4 +1,5 @@ // Copryright 2020 Matheus Xavier all rights reserved. MIT licensed -import { Path, LINUX_SEPS, WINDOWS_SEPS } from "./Path.ts"; +import { Path, LINUX_SEPS, WINDOWS_SEPS, mkDir, mkDirSync, genTmpPath } from "./Path.ts"; +export {Path, LINUX_SEPS, WINDOWS_SEPS, mkDirSync, mkDir, genTmpPath} from "./Path.ts"; export default Path; diff --git a/scripts.yaml b/scripts.yaml new file mode 100644 index 0000000..b95d77a --- /dev/null +++ b/scripts.yaml @@ -0,0 +1,2 @@ +scripts: + pack: deno run --unstable -A https://deno.land/x/denopack@0.9.0/cli.ts -i mod.ts -o mod.js \ No newline at end of file diff --git a/test.ts b/test.ts index 9d78eee..f830485 100644 --- a/test.ts +++ b/test.ts @@ -1,14 +1,42 @@ -import Path from "./mod.ts"; -import { WINDOWS_SEPS } from "./Path.ts"; +import { Path, WINDOWS_SEPS } from "./mod.ts"; +import { + assertEquals, + assertArrayContains, +} from "https://deno.land/std@0.69.0/testing/asserts.ts"; -const winPath = new Path("C:\\Users\\Test\\Documents/myFile.v1.txt", WINDOWS_SEPS); -console.log(winPath.elements); -console.log(winPath.toString()); -console.log(winPath.ext); -console.log(winPath.exists); +Deno.test({ + name: "path explosion", + fn: () => { + let path: Path; + if (Deno.build.os === "windows") { + path = new Path("C:\\etc\\test\\.dotfolder\\test.cfg"); + } else { + path = new Path("/etc/test/.dotfolder/test.cfg"); + } + assertArrayContains( + path.elements, + ["etc", "test", ".dotfolder", "test.cfg"], + "Path does not match the expected result" + ); + }, +}); -const nixPath = new Path("/etc/passwd"); -console.log(nixPath.elements); -console.log(nixPath.toString()); -console.log(nixPath.ext); -console.log(nixPath.exists); +Deno.test({ + name: "path rendering", + fn: () => { + let path: Path; + let expects: string; + if (Deno.build.os === "windows") { + path = new Path("C:\\etc\\test\\.dotfolder\\test.cfg"); + expects = "C:\\etc\\test\\.dotfolder\\test.cfg"; + } else { + path = new Path("/etc/test/.dotfolder/test.cfg"); + expects = "/etc/test/.dotfolder/test.cfg"; + } + assertEquals( + path.toString(), + expects, + "Path does not match the expected result" + ); + }, +});