diff --git a/bin/cli.cjs b/bin/cli.cjs new file mode 100644 index 0000000..7029615 --- /dev/null +++ b/bin/cli.cjs @@ -0,0 +1,93 @@ +#! /usr/bin/env node +/* eslint-disable @typescript-eslint/no-var-requires */ + +const lzString = require("../dist/index.cjs"); +const fs = require("fs"); +const pkg = require("../package.json"); +const { Option, program } = require("commander"); + +function compressContent(format, content) { + switch (format) { + case "base64": + return lzString.compressToBase64(content); + case "encodeduri": + return lzString.compressToEncodedURIComponent(content); + case "raw": + default: + return lzString.compress(content); + case "uint8array": + return lzString.compressToUint8Array(content); + case "utf16": + return lzString.compressToUTF16(content); + } +} + +function decompressContent(format, content) { + switch (format) { + case "base64": + return lzString.decompressFromBase64(content); + case "encodeduri": + return lzString.decompressFromEncodedURIComponent(content); + case "raw": + default: + return lzString.decompress(content); + case "uint8array": + return lzString.decompressFromUint8Array(content); + case "utf16": + return lzString.decompressFromUTF16(content); + } +} + +program + .version(pkg.version) + .description("Use lz-string to compress or decompress a file") + .addOption(new Option("-d, --decompress", "if unset then this will compress")) + .addOption( + new Option("-f, --format ", "formatter to use") + .choices(["base64", "encodeduri", "raw", "uint8array", "utf16"]) + .default("raw"), + ) + .addOption(new Option("-v, --validate", "validate before returning").default(true)) + .addOption(new Option("-o, --output ", "output file")) + .addOption(new Option("-q, --quiet", "don't print any error messages")) + .argument("", "file to process") + .showHelpAfterError() + .action((file, { format, decompress, output, quiet, validate }) => { + if (!fs.existsSync(file)) { + if (!quiet) process.stderr.write(`Unable to find ${file}`); + process.exit(1); + } + try { + fs.accessSync(file, fs.constants.R_OK); + } catch { + if (!quiet) process.stderr.write(`Unable to read ${file}`); + process.exit(1); + } + const unprocessed = fs.readFileSync(file).toString(); + const processed = decompress ? decompressContent(format, unprocessed) : compressContent(format, unprocessed); + + if (validate) { + const validated = decompress ? compressContent(format, processed) : decompressContent(format, processed); + let valid = unprocessed.length === validated.length; + + for (let i = 0; valid && i < unprocessed.length; i++) { + if (unprocessed[i] !== validated[i]) { + valid = false; + } + } + if (!valid) { + if (!quiet) process.stderr.write(`Unable to validate ${file}`); + process.exit(1); + } + } + if (processed == null) { + if (!quiet) process.stderr.write(`Unable to process ${file}`); + process.exit(1); + } + if (output) { + fs.writeFileSync(output, processed, null); + } else { + process.stdout.write(processed); + } + }) + .parse(); diff --git a/package-lock.json b/package-lock.json index 795c6f8..7363f41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,12 @@ "name": "lz-string", "version": "2.0.0-rc.2", "license": "MIT", + "dependencies": { + "commander": "11.1.0" + }, + "bin": { + "lz-string": "bin/cli.cjs" + }, "devDependencies": { "@btmills/prettier": "3.1.0", "@types/node": "20.11.0", @@ -1217,13 +1223,11 @@ } }, "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "optional": true, + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "engines": { - "node": "^12.20.0 || >=14" + "node": ">=16" } }, "node_modules/computeds": { @@ -3606,6 +3610,16 @@ "optionalDependencies": { "commander": "^9.4.1" } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } } } } diff --git a/package.json b/package.json index 6605242..0bbed65 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,9 @@ "compression", "string" ], + "bin": { + "lz-string": "bin/cli.cjs" + }, "exports": { ".": { "types": "./dist/index.d.ts", @@ -42,6 +45,7 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ + "bin", "dist" ], "scripts": { @@ -70,5 +74,8 @@ }, "override": { "prettier": "npm:@btmills/prettier@3.1.0" + }, + "dependencies": { + "commander": "11.1.0" } } diff --git a/src/__tests__/testFunctions.ts b/src/__tests__/testFunctions.ts index 1d3c203..73e7992 100644 --- a/src/__tests__/testFunctions.ts +++ b/src/__tests__/testFunctions.ts @@ -55,7 +55,6 @@ export const test_longString_fn = () => { return testValue.join(" "); }; - /** * This will run a series of tests against each compress / decompress pair. * diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..30f0bfc --- /dev/null +++ b/test.sh @@ -0,0 +1,72 @@ +#! /usr/bin/env bash + +ANSI_RESET="\033[0m" + +FG_RED="\033[31m" +FG_GREEN="\033[32m" +FG_YELLOW="\033[33m" + +Help() { + echo -e "\nUsage: $0 [-u]" + echo "-h: Show this help" + echo "-u: Update any failed tests" + exit +} + +OPT_UPDATE=0 + +# ---===###===--- Code starts here ---===###===--- # + +while getopts ":hu" opt; do + case ${opt} in + "h") Help ;; + "u") OPT_UPDATE=1 ; echo "Updating test files" ;; + esac +done + +# file1, file2, success +compare() { + if cmp -s $1 $2; then + printf " ...${FG_GREEN}%s${ANSI_RESET}" "pass" + return 0 + else + printf " ...${FG_RED}%s${ANSI_RESET}" "fail" + return 1 + fi +} + +# format, folder +process() { + OUTPUT=$(mktemp) + VALIDATE=$(mktemp) + + printf "\nCompress: %-12s " $1 + node bin/cli.cjs -v -q -f $1 testdata/$2/source.bin -o $OUTPUT + if ! compare testdata/$2/$1.bin $OUTPUT; then + node bin/cli.cjs -q -d -f $1 $OUTPUT -o $VALIDATE + if cmp -s $VALIDATE testdata/$2/source.bin; then + if [ ! -f testdata/$2/$1.bin ] || [ $OPT_UPDATE -eq 1 ]; then + cp $OUTPUT testdata/$2/$1.bin + printf " ...${FG_YELLOW}%s${ANSI_RESET}" "updated" + else + printf " ...${FG_RED}%s${ANSI_RESET}" "unsafe" + fi + else + printf " ...${FG_RED}%s${ANSI_RESET}" "validation failed" + fi + fi + + printf "\nDecompress: %-12s " $1 + node bin/cli.cjs -v -q -d -f $1 testdata/$2/$1.bin -o $OUTPUT + compare testdata/$2/source.bin $OUTPUT + + rm $OUTPUT $VALIDATE +} + +# process raw tattoo +process base64 tattoo +process encodeduri tattoo +# process uint8array tattoo +process utf16 tattoo + +printf "\n\nDone\n" \ No newline at end of file diff --git a/testdata/tattoo/base64.bin b/testdata/tattoo/base64.bin new file mode 100644 index 0000000..9d24299 --- /dev/null +++ b/testdata/tattoo/base64.bin @@ -0,0 +1 @@ +CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c/I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4/plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgA \ No newline at end of file diff --git a/testdata/tattoo/base64x.bin b/testdata/tattoo/base64x.bin new file mode 100644 index 0000000..e0615ba --- /dev/null +++ b/testdata/tattoo/base64x.bin @@ -0,0 +1 @@ +CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR 0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c-I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi A4-plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgAAA$$ \ No newline at end of file diff --git a/testdata/tattoo/encodeduri.bin b/testdata/tattoo/encodeduri.bin new file mode 100644 index 0000000..cad9748 --- /dev/null +++ b/testdata/tattoo/encodeduri.bin @@ -0,0 +1 @@ +CIVwTglgdg5gBAFwIYIQezdGAaO0DWeAznlAFYCmAxghQCanqIAWFcR+0u0ECEKWOEih4AtqJBQ2YCkQAOaKEQq5hDKhQA2mklSTb6cAESikVMGjnMkMWUbii0ANzbQmCVkJlIhUBkYoUOBA5ew9XKHwAOjgAFU9Tc0trW10kMDAAT3Y0UTY0ADMWCMJ3TwAjNDpMgHISTUzRKzgoKtlccpAEHLyWIPS2AogDBgB3XmZSQiJkbLku3ApRcvo6Q2hi9k4oGPiUOrhR627TfFlN5FQMOCcIIghyzTZJNbBNjmgY4H1mNBB7tgAVQgLjA9wQtRIAEEnlQ4AAxfRnKDWUTEOBrFyaSyCHzoOQQPSaODmQJojxBUZoMD4EjlbLIMC2PiwTaJCxWGznCndawuOAyUzQQxBcLsXj5Ipiy7oNAxAByFFGDjMHJS50c-I2TCoiiIIF6YrkMlufyIDTgBJgeSgCAAtEMRiqkpzUr4GOERKIIDAwCg2GU2A0mpNWmsiIsXLaQPoLchtvBY5tqmxxh5iqIYkYAOqsES6prpQS8RBoOCaJDKMB28qVwwy66C5z6bgiI6EyaZP7sCgBirgJS4MVEPQZLBDiqaO60MGtlh3El13CjCg1fnhn1SBg+OhgEDwHkYtCyKA1brebTZPlsCRUSaFAp2xnMuAUAoFagIbD2TxEJAQOgs2zVcZBaNBumfCgWUTKBskKTZWjAUxiQ+fMtB0XAiDLLsQEORQzx7NgfGxbp4OgAoK3EARFBiABJEQCjML84FrZQGEUTZjTQDQiBIQ8VxqUCmJjS9gnuWBlzYOh8Ig5gCGKUDxm0FiiNg0gKKQKi+A4-plLUPBuipEBNG3GgRItFZfD4O1yMo0x0CyKIgA \ No newline at end of file diff --git a/testdata/tattoo/source.bin b/testdata/tattoo/source.bin new file mode 100644 index 0000000..62cdd5e --- /dev/null +++ b/testdata/tattoo/source.bin @@ -0,0 +1 @@ +During tattooing, ink is injected into the skin, initiating an immune response, and cells called "macrophages" move into the area and "eat up" the ink. The macrophages carry some of the ink to the body's lymph nodes, but some that are filled with ink stay put, embedded in the skin. That's what makes the tattoo visible under the skin. Dalhousie Uiversity's Alec Falkenham is developing a topical cream that works by targeting the macrophages that have remained at the site of the tattoo. New macrophages move in to consume the previously pigment-filled macrophages and then migrate to the lymph nodes, eventually taking all the dye with them. "When comparing it to laser-based tattoo removal, in which you see the burns, the scarring, the blisters, in this case, we've designed a drug that doesn't really have much off-target effect," he said. "We're not targeting any of the normal skin cells, so you won't see a lot of inflammation. In fact, based on the process that we're actually using, we don't think there will be any inflammation at all and it would actually be anti-inflammatory. \ No newline at end of file diff --git a/testdata/tattoo/uint8array.bin b/testdata/tattoo/uint8array.bin new file mode 100644 index 0000000..4432b97 Binary files /dev/null and b/testdata/tattoo/uint8array.bin differ diff --git a/testdata/tattoo/utf16.bin b/testdata/tattoo/utf16.bin new file mode 100644 index 0000000..ab82348 --- /dev/null +++ b/testdata/tattoo/utf16.bin @@ -0,0 +1 @@ +Ѣ尳䅌ހ猠ဥ恣Ȱ㶻冠㒖䃶㰦㨅KÆƬࡰӴ窨Ð圱緅洤с▮ऴḠ孱ၰ泠⤰“⊤ⅷᡣᔪV䵄咳㟳䀤┴䕬഼獄ᣒ䜂䗀Γ㚤悵䡬剁⠬䙈⢐ၙ㶧嗪ྠσ䀠啝✆䮋ⷎ䤬ؠԗ㇂䓸栠㌶ҁᷳ怤㍣劄ǨⒺ䳱ᖼਪ孋䝉@猒ⱡ瓖Ťá䀮瘆㊲ࢩ䣹㥎渥ᑼ彧⑖䌷奘偑碴ᵼᒚ淺㟥䪏ᅰᢐ✨ᄤ᳓⛩፻ʌ㧀㇠綆ᨨἍ䀢呀᜸ྐ↊ሠࡄ祰瀠౿⍙⃶⢸䎡噎⚲搰紈Ტϲ㒐㦰፤㱡⣭̣灄㥻ᙡ䃖ἶӺሶᖦ杘⧽㖥掠擆㐰戫䌌⼿቉䖷㨭ƨ’ੈ愃ᡙᓙ椇牖☵ࣂၠ庸坁䦎㾱ó䀩ᠾ┠†婁䒂啅᳴埠掤≱ȣƠ⡖ೆ恔䵩嗆失ଷᛲЈᜄᮏ˧ᮊ䶮ᢙ䕱ل〡櫌ࢷ⪋劢⼱͡悺ሹ⌡清▐憷⺢玿✀䑧⅒㓩缌Ԡᣋ䁪⸬⩁琹ᘨ㣊㒗⴬ඌ増ॎ巢䘴ൿ伬絲̧揁䁀㰧䣥僒ᐦ國㳺㙯䮀⑴⓰偉涬獎Àਥ㔡ۣ沾ᄩ¡桌涺圹˱傎䴘⠶⢹₌䡲㙶䘢䲂∓獍ς灂ٹ㬰࢒ᑓ揹堿ණ㪘ᴠਪ湀ѥ̰iࢠ⣬᠇Ƌ㊠愴⛬㓰ڤ⁨∂岊⠴抃☌਎Ⱜ峸ᴯ䉀猠↪⠾᧔଴⎀椡⊰ᔷ恘罬勴ḭ棉ࠩ凗ഢቍ૫焘᷎⍈榮䃒ᑤ \ No newline at end of file