Compare commits

..

9 Commits

Author SHA1 Message Date
maudnals
87eae143ef Refactored to reflect the strict-csp API change to an options object 2022-01-10 16:36:36 +01:00
maudnals
8aeea1dc04 Enabled unsafe eval 2021-12-23 09:29:04 +01:00
maudnals
8a860f285c Added comments 2021-12-15 15:39:07 +01:00
maudnals
dd2b2c361f Added comments 2021-12-15 15:38:28 +01:00
maudnals
e673c9c772 Import strict-csp from npm 2021-12-15 15:29:43 +01:00
maudnals
c484750779 Merge branch 'dev' into maudnals-csp 2021-12-15 11:09:19 +01:00
maudnals
58d467fbba Added CSP generation code 2021-09-15 12:35:03 +02:00
maudnals
b2465ebe48 Added lib-csp (temp until available as npm pckg) 2021-09-15 12:32:39 +02:00
maudnals
8ea8d05706 Added needed dependency for CSP 2021-09-15 12:32:02 +02:00
44 changed files with 665 additions and 159 deletions

4
.gitattributes vendored
View File

@@ -1,2 +1,2 @@
/codecs/**/*.js linguist-generated -diff /codecs/**/*.js linguist-generated=true
/codecs/*/pkg*/*.d.ts linguist-generated /codecs/*/pkg*/*.d.ts linguist-generated=true

View File

@@ -1,7 +1,3 @@
# Project no longer maintained
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
# Squoosh CLI # Squoosh CLI
Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once. Squoosh CLI is an _experimental_ way to run all the codecs you know from the [Squoosh] web app on your command line using WebAssembly. The Squoosh CLI uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.

16
cli/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.7.3", "version": "0.7.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.7.3", "version": "0.7.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@squoosh/lib": "^0.4.0", "@squoosh/lib": "^0.4.0",
@@ -36,9 +36,9 @@
} }
}, },
"node_modules/ansi-regex": { "node_modules/ansi-regex": {
"version": "5.0.1", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
} }
@@ -363,9 +363,9 @@
} }
}, },
"ansi-regex": { "ansi-regex": {
"version": "5.0.1", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
}, },
"ansi-styles": { "ansi-styles": {
"version": "4.3.0", "version": "4.3.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@squoosh/cli", "name": "@squoosh/cli",
"version": "0.7.3", "version": "0.7.2",
"description": "A CLI for Squoosh", "description": "A CLI for Squoosh",
"public": true, "public": true,
"type": "module", "type": "module",

View File

@@ -1,5 +1,5 @@
CODEC_URL = https://github.com/libjxl/libjxl.git CODEC_URL = https://github.com/libjxl/libjxl.git
CODEC_VERSION = 9f544641ec83f6abd9da598bdd08178ee8a003e0 CODEC_VERSION = v0.5
CODEC_DIR = node_modules/jxl CODEC_DIR = node_modules/jxl
CODEC_BUILD_ROOT := $(CODEC_DIR)/build CODEC_BUILD_ROOT := $(CODEC_DIR)/build
CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt CODEC_MT_BUILD_DIR := $(CODEC_BUILD_ROOT)/mt
@@ -75,9 +75,6 @@ $(CODEC_MT_SIMD_BUILD_DIR)/Makefile: CXXFLAGS+=-msimd128
-DCMAKE_CROSSCOMPILING_EMULATOR=node \ -DCMAKE_CROSSCOMPILING_EMULATOR=node \
-B $(@D) \ -B $(@D) \
$(<D) $(<D)
emcc -Wall -O3 -o $(CODEC_DIR)/third_party/skcms/skcms.cc.o -I$(CODEC_DIR)/third_party/skcms -c $(CODEC_DIR)/third_party/skcms/skcms.cc
llvm-ar rc $(CODEC_BUILD_DIR)/third_party/libskcms.a $(CODEC_DIR)/third_party/skcms/skcms.cc.o
rm $(CODEC_DIR)/third_party/skcms/skcms.cc.o
$(CODEC_DIR)/CMakeLists.txt: $(CODEC_DIR)/CMakeLists.txt:
$(RM) -r $(@D) $(RM) -r $(@D)

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -4,7 +4,6 @@
#include "lib/jxl/base/thread_pool_internal.h" #include "lib/jxl/base/thread_pool_internal.h"
#include "lib/jxl/enc_external_image.h" #include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_file.h" #include "lib/jxl/enc_file.h"
#include "lib/jxl/enc_color_management.h"
using namespace emscripten; using namespace emscripten;
@@ -18,7 +17,6 @@ struct JXLOptions {
bool lossyPalette; bool lossyPalette;
size_t decodingSpeedTier; size_t decodingSpeedTier;
float photonNoiseIso; float photonNoiseIso;
bool lossyModular;
}; };
val encode(std::string image, int width, int height, JXLOptions options) { val encode(std::string image, int width, int height, JXLOptions options) {
@@ -52,16 +50,11 @@ val encode(std::string image, int width, int height, JXLOptions options) {
float quality = options.quality; float quality = options.quality;
// Quality settings roughly match libjpeg qualities. // Quality settings roughly match libjpeg qualities.
if (options.lossyModular || quality == 100) { if (quality < 7 || quality == 100) {
cparams.modular_mode = true; cparams.modular_mode = true;
// Internal modular quality to roughly match VarDCT size. // Internal modular quality to roughly match VarDCT size.
if (quality < 7) {
cparams.quality_pair.first = cparams.quality_pair.second = cparams.quality_pair.first = cparams.quality_pair.second =
std::min(35 + (quality - 7) * 3.0f, 100.0f); std::min(35 + (quality - 7) * 3.0f, 100.0f);
} else {
cparams.quality_pair.first = cparams.quality_pair.second =
std::min(35 + (quality - 7) * 65.f / 93.f, 100.0f);
}
} else { } else {
cparams.modular_mode = false; cparams.modular_mode = false;
if (quality >= 30) { if (quality >= 30) {
@@ -98,14 +91,14 @@ val encode(std::string image, int width, int height, JXLOptions options) {
jxl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(image.data()), image.size()), width, jxl::Span<const uint8_t>(reinterpret_cast<const uint8_t*>(image.data()), image.size()), width,
height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true, height, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/8, /*endiannes=*/JXL_LITTLE_ENDIAN, /*alpha_is_premultiplied=*/false, /*bits_per_sample=*/8, /*endiannes=*/JXL_LITTLE_ENDIAN,
/*flipped_y=*/false, pool_ptr, main, /*(only true if bits_per_sample==32) float_in=*/false); /*flipped_y=*/false, pool_ptr, main);
if (!result) { if (!result) {
return val::null(); return val::null();
} }
auto js_result = val::null(); auto js_result = val::null();
if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, jxl::GetJxlCms(), /*aux=*/nullptr, pool_ptr)) { if (EncodeFile(cparams, &io, &passes_enc_state, &bytes, /*aux=*/nullptr, pool_ptr)) {
js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data())); js_result = Uint8Array.new_(typed_memory_view(bytes.size(), bytes.data()));
} }
@@ -120,7 +113,6 @@ EMSCRIPTEN_BINDINGS(my_module) {
.field("lossyPalette", &JXLOptions::lossyPalette) .field("lossyPalette", &JXLOptions::lossyPalette)
.field("decodingSpeedTier", &JXLOptions::decodingSpeedTier) .field("decodingSpeedTier", &JXLOptions::decodingSpeedTier)
.field("photonNoiseIso", &JXLOptions::photonNoiseIso) .field("photonNoiseIso", &JXLOptions::photonNoiseIso)
.field("lossyModular", &JXLOptions::lossyModular)
.field("epf", &JXLOptions::epf); .field("epf", &JXLOptions::epf);
function("encode", &encode); function("encode", &encode);

View File

@@ -6,7 +6,6 @@ export interface EncodeOptions {
lossyPalette: boolean; lossyPalette: boolean;
decodingSpeedTier: number; decodingSpeedTier: number;
photonNoiseIso: number; photonNoiseIso: number;
lossyModular: boolean;
} }
export interface JXLModule extends EmscriptenWasm.Module { export interface JXLModule extends EmscriptenWasm.Module {

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -1,7 +1,3 @@
# Project no longer maintained
Unfortunately, due to a few people leaving the team, and staffing issues resulting from the current economic climate (ugh), this package is no longer actively maintained. I know that sucks, but there simply isn't the time & people to work on this. If anyone from the community wants to fork it, you have my blessing. The [squoosh.app](https://squoosh.app) will continue to be supported and improved.
# libSquoosh # libSquoosh
libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once. libSquoosh is an _experimental_ way to run all the codecs you know from the [Squoosh] web app directly inside your own JavaScript program. libSquoosh uses a worker pool to parallelize processing images. This way you can apply the same codec to many images at once.
@@ -47,15 +43,19 @@ The returned `image` object is a representation of the original image, that you
When an image has been ingested, you can start preprocessing it and encoding it to other formats. This example will resize the image and then encode it to a `.jpg` and `.jxl` image: When an image has been ingested, you can start preprocessing it and encoding it to other formats. This example will resize the image and then encode it to a `.jpg` and `.jxl` image:
```js ```js
await image.decoded; //Wait until the image is decoded before running preprocessors.
const preprocessOptions = { const preprocessOptions = {
//When both width and height are specified, the image resized to specified size. //When both width and height are specified, the image resized to specified size.
resize: { resize: {
enabled: true,
width: 100, width: 100,
height: 50, height: 50,
}, },
/* /*
//When either width or height is specified, the image resized to specified size keeping aspect ratio. //When either width or height is specified, the image resized to specified size keeping aspect ratio.
resize: { resize: {
enabled: true,
width: 100, width: 100,
} }
*/ */
@@ -68,7 +68,7 @@ const encodeOptions = {
quality: 90, quality: 90,
}, },
}; };
const result = await image.encode(encodeOptions); await image.encode(encodeOptions);
``` ```
The default values for each option can be found in the [`codecs.ts`][codecs.ts] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified there. _Better documentation is needed here._ The default values for each option can be found in the [`codecs.ts`][codecs.ts] file under `defaultEncoderOptions`. Every unspecified value will use the default value specified there. _Better documentation is needed here._
@@ -92,7 +92,7 @@ When you have encoded an image, you normally want to write it to a file.
This example takes an image that has been encoded as a `jpg` and writes it to a file: This example takes an image that has been encoded as a `jpg` and writes it to a file:
```js ```js
const rawEncodedImage = image.encodedWith.mozjpeg.binary; const rawEncodedImage = (await image.encodedWith.mozjpeg).binary;
fs.writeFile('/path/to/new/image.jpg', rawEncodedImage); fs.writeFile('/path/to/new/image.jpg', rawEncodedImage);
``` ```
@@ -103,7 +103,10 @@ This example iterates through all encoded versions of the image and writes them
const newImagePath = '/path/to/image.'; //extension is added automatically const newImagePath = '/path/to/image.'; //extension is added automatically
for (const encodedImage of Object.values(image.encodedWith)) { for (const encodedImage of Object.values(image.encodedWith)) {
fs.writeFile(newImagePath + encodedImage.extension, encodedImage.binary); fs.writeFile(
newImagePath + (await encodedImage).extension,
(await encodedImage).binary,
);
} }
``` ```
@@ -132,7 +135,7 @@ console.log(await image.decoded);
Information about an encoded image can be found at `Image.encodedWith[encoderName]`. It looks something like this: Information about an encoded image can be found at `Image.encodedWith[encoderName]`. It looks something like this:
```js ```js
console.log(image.encodedWith.jxl); console.log(await image.encodedWith.jxl);
// Returns: // Returns:
{ {
optionsUsed: { optionsUsed: {

View File

@@ -1,26 +0,0 @@
const fs = require('fs');
const del = require('del');
const path = require('path');
const tsOutputDir = path.resolve('..', '.tmp', 'ts', 'libsquoosh');
const tsOutputSourceDir = path.join(tsOutputDir, 'src');
const buildDir = path.resolve('build');
(async () => {
await del(path.join(buildDir, '*.d.ts'));
const files = await fs.promises.readdir(tsOutputSourceDir);
const movePromises = [];
for (const file of files) {
if (file.endsWith('d.ts') || file.endsWith('d.ts.map')) {
movePromises.push(
fs.promises.rename(
path.join(tsOutputSourceDir, file),
path.join(buildDir, file),
),
);
}
}
// We need to remove `tsconfig.tsbuildinfo` otherwise TS does not emit unchanged `.d.ts` files
await del([path.join(tsOutputDir, 'tsconfig.tsbuildinfo')], { force: true });
})();

View File

@@ -1,12 +1,12 @@
{ {
"name": "@squoosh/lib", "name": "@squoosh/lib",
"version": "0.5.2", "version": "0.4.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@squoosh/lib", "name": "@squoosh/lib",
"version": "0.5.2", "version": "0.4.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"wasm-feature-detect": "^1.2.11", "wasm-feature-detect": "^1.2.11",

View File

@@ -1,15 +1,14 @@
{ {
"name": "@squoosh/lib", "name": "@squoosh/lib",
"version": "0.5.2", "version": "0.4.0",
"description": "A Node library for Squoosh", "description": "A Node library for Squoosh",
"public": true, "public": true,
"main": "./build/index.js", "main": "./build/index.js",
"types": "./build/index.d.ts",
"files": [ "files": [
"/build/*" "/build/*"
], ],
"scripts": { "scripts": {
"build": "rollup -c && node lib/move-d-ts.js" "build": "rollup -c"
}, },
"keywords": [], "keywords": [],
"author": "Google Chrome Developers <chromium-dev@google.com>", "author": "Google Chrome Developers <chromium-dev@google.com>",

View File

@@ -419,7 +419,6 @@ export const codecs = {
lossyPalette: false, lossyPalette: false,
decodingSpeedTier: 0, decodingSpeedTier: 0,
photonNoiseIso: 0, photonNoiseIso: 0,
lossyModular: false,
}, },
autoOptimize: { autoOptimize: {
option: 'quality', option: 'quality',

View File

@@ -22,10 +22,9 @@ type EncoderKey = keyof typeof encoders;
type PreprocessorKey = keyof typeof preprocessors; type PreprocessorKey = keyof typeof preprocessors;
type PreprocessOptions = { type PreprocessOptions = {
resize?: Partial<Omit<ResizeOptions, 'width' | 'height'>> & resize?: ResizeOptions;
(Pick<ResizeOptions, 'width'> | Pick<ResizeOptions, 'height'>); quant?: QuantOptions;
quant?: Partial<QuantOptions>; rotate?: RotateOptions;
rotate?: Partial<RotateOptions>;
}; };
type EncodeResult = { type EncodeResult = {
optionsUsed: object; optionsUsed: object;

View File

@@ -3,8 +3,7 @@
"compilerOptions": { "compilerOptions": {
"lib": ["esnext"], "lib": ["esnext"],
"types": ["node"], "types": ["node"],
"allowJs": true, "allowJs": true
"declaration": true
}, },
"include": ["src/**/*", "../codecs/**/*"] "include": ["src/**/*", "../codecs/**/*"]
} }

451
package-lock.json generated
View File

@@ -5,9 +5,11 @@
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "squoosh",
"version": "2.0.0", "version": "2.0.0",
"license": "apache-2.0", "license": "apache-2.0",
"dependencies": { "dependencies": {
"strict-csp": "^1.0.4",
"wasm-feature-detect": "^1.2.11" "wasm-feature-detect": "^1.2.11"
}, },
"devDependencies": { "devDependencies": {
@@ -286,6 +288,14 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/@types/cheerio": {
"version": "0.22.30",
"resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.30.tgz",
"integrity": "sha512-t7ZVArWZlq3dFa9Yt33qFBQIK4CQd1Q3UJp0V+UhP6vgLWLM6Qug7vZuRSGXg45zXeB1Fm5X2vmBkEX58LV2Tw==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/color-name": { "node_modules/@types/color-name": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -329,8 +339,7 @@
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "16.11.1", "version": "16.11.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
"dev": true
}, },
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
@@ -594,8 +603,7 @@
"node_modules/boolbase": { "node_modules/boolbase": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
"dev": true
}, },
"node_modules/boxen": { "node_modules/boxen": {
"version": "1.3.0", "version": "1.3.0",
@@ -866,6 +874,144 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/cheerio": {
"version": "1.0.0-rc.10",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
"integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==",
"dependencies": {
"cheerio-select": "^1.5.0",
"dom-serializer": "^1.3.2",
"domhandler": "^4.2.0",
"htmlparser2": "^6.1.0",
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",
"tslib": "^2.2.0"
},
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
}
},
"node_modules/cheerio-select": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz",
"integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==",
"dependencies": {
"css-select": "^4.1.3",
"css-what": "^5.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0",
"domutils": "^2.7.0"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cheerio-select/node_modules/css-select": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.0.tgz",
"integrity": "sha512-6YVG6hsH9yIb/si3Th/is8Pex7qnVHO6t7q7U6TIUnkQASGbS8tnUDBftnPynLNnuUl/r2+PTd0ekiiq7R0zJw==",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^5.1.0",
"domhandler": "^4.3.0",
"domutils": "^2.8.0",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cheerio-select/node_modules/css-what": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
"integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
"engines": {
"node": ">= 6"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/cheerio-select/node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/cheerio-select/node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/cheerio-select/node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/cheerio-select/node_modules/nth-check": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"dependencies": {
"boolbase": "^1.0.0"
},
"funding": {
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/cheerio/node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/cheerio/node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/cheerio/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/clean-stack": { "node_modules/clean-stack": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -1975,6 +2121,31 @@
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"dev": true "dev": true
}, },
"node_modules/domhandler": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
"integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
"dependencies": {
"domelementtype": "^2.2.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domhandler/node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/domutils": { "node_modules/domutils": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@@ -2066,8 +2237,7 @@
"node_modules/entities": { "node_modules/entities": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
"dev": true
}, },
"node_modules/error-ex": { "node_modules/error-ex": {
"version": "1.3.2", "version": "1.3.2",
@@ -2438,6 +2608,61 @@
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
"dev": true "dev": true
}, },
"node_modules/htmlparser2": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"funding": [
"https://github.com/fb55/htmlparser2?sponsor=1",
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
],
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.0.0",
"domutils": "^2.5.2",
"entities": "^2.0.0"
}
},
"node_modules/htmlparser2/node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/htmlparser2/node_modules/domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fb55"
}
]
},
"node_modules/htmlparser2/node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/human-signals": { "node_modules/human-signals": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -3678,6 +3903,19 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
"dependencies": {
"parse5": "^6.0.1"
}
},
"node_modules/path-is-absolute": { "node_modules/path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -7954,6 +8192,15 @@
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true "dev": true
}, },
"node_modules/strict-csp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/strict-csp/-/strict-csp-1.0.4.tgz",
"integrity": "sha512-CDAq0Zpg3XtJtUxjuOgKK2UgBnW03HDIFyb0rwpxNuQdYC8JquMfnGGhNZcbq22iYzVg6eCgqBScebA6MtwRdg==",
"dependencies": {
"@types/cheerio": "^0.22.23",
"cheerio": "^1.0.0-rc.5"
}
},
"node_modules/string-argv": { "node_modules/string-argv": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",
@@ -8874,6 +9121,14 @@
} }
} }
}, },
"@types/cheerio": {
"version": "0.22.30",
"resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.30.tgz",
"integrity": "sha512-t7ZVArWZlq3dFa9Yt33qFBQIK4CQd1Q3UJp0V+UhP6vgLWLM6Qug7vZuRSGXg45zXeB1Fm5X2vmBkEX58LV2Tw==",
"requires": {
"@types/node": "*"
}
},
"@types/color-name": { "@types/color-name": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
@@ -8917,8 +9172,7 @@
"@types/node": { "@types/node": {
"version": "16.11.1", "version": "16.11.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
"dev": true
}, },
"@types/parse-json": { "@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
@@ -9141,8 +9395,7 @@
"boolbase": { "boolbase": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
"dev": true
}, },
"boxen": { "boxen": {
"version": "1.3.0", "version": "1.3.0",
@@ -9357,6 +9610,106 @@
"supports-color": "^7.1.0" "supports-color": "^7.1.0"
} }
}, },
"cheerio": {
"version": "1.0.0-rc.10",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz",
"integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==",
"requires": {
"cheerio-select": "^1.5.0",
"dom-serializer": "^1.3.2",
"domhandler": "^4.2.0",
"htmlparser2": "^6.1.0",
"parse5": "^6.0.1",
"parse5-htmlparser2-tree-adapter": "^6.0.1",
"tslib": "^2.2.0"
},
"dependencies": {
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"cheerio-select": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz",
"integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==",
"requires": {
"css-select": "^4.1.3",
"css-what": "^5.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0",
"domutils": "^2.7.0"
},
"dependencies": {
"css-select": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.0.tgz",
"integrity": "sha512-6YVG6hsH9yIb/si3Th/is8Pex7qnVHO6t7q7U6TIUnkQASGbS8tnUDBftnPynLNnuUl/r2+PTd0ekiiq7R0zJw==",
"requires": {
"boolbase": "^1.0.0",
"css-what": "^5.1.0",
"domhandler": "^4.3.0",
"domutils": "^2.8.0",
"nth-check": "^2.0.1"
}
},
"css-what": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
"integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
},
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
},
"nth-check": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
"integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
"requires": {
"boolbase": "^1.0.0"
}
}
}
},
"clean-stack": { "clean-stack": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -10278,6 +10631,21 @@
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
"dev": true "dev": true
}, },
"domhandler": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
"integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
"requires": {
"domelementtype": "^2.2.0"
},
"dependencies": {
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
}
}
},
"domutils": { "domutils": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@@ -10353,8 +10721,7 @@
"entities": { "entities": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
"integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
"dev": true
}, },
"error-ex": { "error-ex": {
"version": "1.3.2", "version": "1.3.2",
@@ -10666,6 +11033,44 @@
"integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
"dev": true "dev": true
}, },
"htmlparser2": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
"integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.0.0",
"domutils": "^2.5.2",
"entities": "^2.0.0"
},
"dependencies": {
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
"integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
"requires": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
}
},
"domelementtype": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
"integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"requires": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
}
}
}
},
"human-signals": { "human-signals": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -11662,6 +12067,19 @@
"lines-and-columns": "^1.1.6" "lines-and-columns": "^1.1.6"
} }
}, },
"parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
},
"parse5-htmlparser2-tree-adapter": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
"requires": {
"parse5": "^6.0.1"
}
},
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -15261,6 +15679,15 @@
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true "dev": true
}, },
"strict-csp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/strict-csp/-/strict-csp-1.0.4.tgz",
"integrity": "sha512-CDAq0Zpg3XtJtUxjuOgKK2UgBnW03HDIFyb0rwpxNuQdYC8JquMfnGGhNZcbq22iYzVg6eCgqBScebA6MtwRdg==",
"requires": {
"@types/cheerio": "^0.22.23",
"cheerio": "^1.0.0-rc.5"
}
},
"string-argv": { "string-argv": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz",

View File

@@ -54,6 +54,7 @@
"*.rs": "rustfmt" "*.rs": "rustfmt"
}, },
"dependencies": { "dependencies": {
"strict-csp": "^1.0.4",
"wasm-feature-detect": "^1.2.11" "wasm-feature-detect": "^1.2.11"
} }
} }

View File

@@ -11,11 +11,7 @@ export default class Checkbox extends Component<Props, State> {
return ( return (
<div class={style.checkbox}> <div class={style.checkbox}>
{props.checked ? ( {props.checked ? (
props.disabled ? (
<CheckedIcon class={`${style.icon} ${style.disabled}`} />
) : (
<CheckedIcon class={`${style.icon} ${style.checked}`} /> <CheckedIcon class={`${style.icon} ${style.checked}`} />
)
) : ( ) : (
<UncheckedIcon class={style.icon} /> <UncheckedIcon class={style.icon} />
)} )}

View File

@@ -20,7 +20,3 @@
.checked { .checked {
fill: var(--main-theme-color); fill: var(--main-theme-color);
} }
.disabled {
fill: var(--dark-gray);
}

View File

@@ -40,8 +40,7 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
} }
.scrubber { .scrubber {
display: grid; display: flex;
align-content: center;
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@@ -55,6 +54,10 @@ two-up[legacy-clip-compat] > :not(.two-up-handle) {
padding: 0 calc(var(--thumb-size) * 0.24); padding: 0 calc(var(--thumb-size) * 0.24);
} }
.scrubber svg {
flex: 1;
}
.arrow-left { .arrow-left {
fill: var(--pink); fill: var(--pink);
} }

View File

@@ -49,7 +49,6 @@
align-self: center; align-self: center;
padding: 9px 66px; padding: 9px 66px;
position: relative; position: relative;
gap: 6px;
/* Allow clicks to fall through to the pinch zoom area */ /* Allow clicks to fall through to the pinch zoom area */
pointer-events: none; pointer-events: none;
@@ -69,6 +68,7 @@
display: flex; display: flex;
position: relative; position: relative;
z-index: 100; z-index: 100;
margin: 0 3px;
} }
.button, .button,
@@ -95,7 +95,6 @@
.button { .button {
color: #fff; color: #fff;
margin: 0;
&:hover { &:hover {
background: rgba(50, 50, 50, 0.92); background: rgba(50, 50, 50, 0.92);
@@ -115,7 +114,7 @@
.last-button { .last-button {
composes: button; composes: button;
border-radius: 0 6px 6px 0; border-radius: 0 6px 6px 0;
border-right-width: 1px; border-left-width: 1px;
} }
.zoom { .zoom {

View File

@@ -114,7 +114,7 @@ async function decodeImage(
} }
} }
// Otherwise fall through and try built-in decoding for a laugh. // Otherwise fall through and try built-in decoding for a laugh.
return await builtinDecode(signal, blob); return await builtinDecode(signal, blob, mimeType);
} catch (err) { } catch (err) {
if (err instanceof Error && err.name === 'AbortError') throw err; if (err instanceof Error && err.name === 'AbortError') throw err;
console.log(err); console.log(err);

View File

@@ -63,17 +63,35 @@ interface DrawableToImageDataOptions {
sh?: number; sh?: number;
} }
function getWidth(
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
): number {
if ('displayWidth' in drawable) {
return drawable.displayWidth;
}
return drawable.width;
}
function getHeight(
drawable: ImageBitmap | HTMLImageElement | VideoFrame,
): number {
if ('displayHeight' in drawable) {
return drawable.displayHeight;
}
return drawable.height;
}
export function drawableToImageData( export function drawableToImageData(
drawable: ImageBitmap | HTMLImageElement, drawable: ImageBitmap | HTMLImageElement | VideoFrame,
opts: DrawableToImageDataOptions = {}, opts: DrawableToImageDataOptions = {},
): ImageData { ): ImageData {
const { const {
width = drawable.width, width = getWidth(drawable),
height = drawable.height, height = getHeight(drawable),
sx = 0, sx = 0,
sy = 0, sy = 0,
sw = drawable.width, sw = getWidth(drawable),
sh = drawable.height, sh = getHeight(drawable),
} = opts; } = opts;
// Make canvas same size as image // Make canvas same size as image

View File

@@ -10,6 +10,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import * as WebCodecs from '../util/web-codecs';
import { drawableToImageData } from './canvas'; import { drawableToImageData } from './canvas';
/** If render engine is Safari */ /** If render engine is Safari */
@@ -137,7 +139,15 @@ export async function blobToImg(blob: Blob): Promise<HTMLImageElement> {
export async function builtinDecode( export async function builtinDecode(
signal: AbortSignal, signal: AbortSignal,
blob: Blob, blob: Blob,
mimeType: string,
): Promise<ImageData> { ): Promise<ImageData> {
// If WebCodecs are supported, use that.
if (await WebCodecs.isTypeSupported(mimeType)) {
assertSignal(signal);
try {
return await abortable(signal, WebCodecs.decode(blob, mimeType));
} catch (e) {}
}
assertSignal(signal); assertSignal(signal);
// Prefer createImageBitmap as it's the off-thread option for Firefox. // Prefer createImageBitmap as it's the off-thread option for Firefox.

View File

@@ -0,0 +1,33 @@
import { drawableToImageData } from '../canvas';
const hasImageDecoder = typeof ImageDecoder !== 'undefined';
export async function isTypeSupported(mimeType: string): Promise<boolean> {
if (!hasImageDecoder) return false;
// Some old versions of this API threw here.
// It only impacted folks with experimental web platform flags enabled in Chrome 90.
// The API was updated in Chrome 91.
try {
return await ImageDecoder.isTypeSupported(mimeType);
} catch (err) {
return false;
}
}
export async function decode(
blob: Blob | File,
mimeType: string,
): Promise<ImageData> {
if (!hasImageDecoder) {
throw Error(
`This browser does not support ImageDecoder. This function should not have been called.`,
);
}
const decoder = new ImageDecoder({
type: mimeType,
// Non-obvious way to turn an Blob into a ReadableStream
data: new Response(blob).body!,
});
const { image } = await decoder.decode();
return drawableToImageData(image);
}

View File

@@ -0,0 +1,60 @@
interface ImageDecoderInit {
type: string;
data: BufferSource | ReadableStream;
premultiplyAlpha?: PremultiplyAlpha;
colorSpaceConversion?: ColorSpaceConversion;
desiredWidth?: number;
desiredHeight?: number;
preferAnimation?: boolean;
}
interface ImageDecodeOptions {
frameIndex: number;
completeFramesOnly: boolean;
}
interface ImageDecodeResult {
image: VideoFrame;
complete: boolean;
}
// I didnt do all the types because the class is kinda complex.
// I focused on what we need.
// See https://w3c.github.io/webcodecs/#videoframe
declare class VideoFrame {
displayWidth: number;
displayHeight: number;
}
// Add VideoFrame to canvas drawImage()
interface CanvasDrawImage {
drawImage(
image: CanvasImageSource | VideoFrame,
dx: number,
dy: number,
): void;
drawImage(
image: CanvasImageSource | VideoFrame,
dx: number,
dy: number,
dw: number,
dh: number,
): void;
drawImage(
image: CanvasImageSource | VideoFrame,
sx: number,
sy: number,
sw: number,
sh: number,
dx: number,
dy: number,
dw: number,
dh: number,
): void;
}
declare class ImageDecoder {
static isTypeSupported(type: string): Promise<boolean>;
constructor(desc: ImageDecoderInit);
decode(opts?: Partial<ImageDecodeOptions>): Promise<ImageDecodeResult>;
}

View File

@@ -30,7 +30,6 @@ interface State {
autoEdgePreservingFilter: boolean; autoEdgePreservingFilter: boolean;
decodingSpeedTier: number; decodingSpeedTier: number;
photonNoiseIso: number; photonNoiseIso: number;
alternativeLossy: boolean;
} }
export class Options extends Component<Props, State> { export class Options extends Component<Props, State> {
@@ -56,7 +55,6 @@ export class Options extends Component<Props, State> {
autoEdgePreservingFilter: options.epf === -1, autoEdgePreservingFilter: options.epf === -1,
decodingSpeedTier: options.decodingSpeedTier, decodingSpeedTier: options.decodingSpeedTier,
photonNoiseIso: options.photonNoiseIso, photonNoiseIso: options.photonNoiseIso,
alternativeLossy: options.lossyModular,
}; };
} }
@@ -98,7 +96,6 @@ export class Options extends Component<Props, State> {
lossyPalette: optionState.lossless ? optionState.slightLoss : false, lossyPalette: optionState.lossless ? optionState.slightLoss : false,
decodingSpeedTier: optionState.decodingSpeedTier, decodingSpeedTier: optionState.decodingSpeedTier,
photonNoiseIso: optionState.photonNoiseIso, photonNoiseIso: optionState.photonNoiseIso,
lossyModular: optionState.quality < 7 ? true : optionState.alternativeLossy,
}; };
// Updating options, so we don't recalculate in getDerivedStateFromProps. // Updating options, so we don't recalculate in getDerivedStateFromProps.
@@ -125,7 +122,6 @@ export class Options extends Component<Props, State> {
autoEdgePreservingFilter, autoEdgePreservingFilter,
decodingSpeedTier, decodingSpeedTier,
photonNoiseIso, photonNoiseIso,
alternativeLossy,
}: State, }: State,
) { ) {
// I'm rendering both lossy and lossless forms, as it becomes much easier when // I'm rendering both lossy and lossless forms, as it becomes much easier when
@@ -166,14 +162,6 @@ export class Options extends Component<Props, State> {
Quality: Quality:
</Range> </Range>
</div> </div>
<label class={style.optionToggle}>
Alternative lossy mode
<Checkbox
checked={quality < 7 ? true : alternativeLossy}
disabled={quality < 7}
onChange={this._inputChange('alternativeLossy', 'boolean')}
/>
</label>
<label class={style.optionToggle}> <label class={style.optionToggle}>
Auto edge filter Auto edge filter
<Checkbox <Checkbox
@@ -235,7 +223,7 @@ export class Options extends Component<Props, State> {
</label> </label>
<div class={style.optionOneCell}> <div class={style.optionOneCell}>
<Range <Range
min="1" min="3"
max="9" max="9"
value={effort} value={effort}
onInput={this._inputChange('effort', 'number')} onInput={this._inputChange('effort', 'number')}

View File

@@ -25,5 +25,4 @@ export const defaultOptions: EncodeOptions = {
lossyPalette: false, lossyPalette: false,
decodingSpeedTier: 0, decodingSpeedTier: 0,
photonNoiseIso: 0, photonNoiseIso: 0,
lossyModular: false,
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -19,39 +19,15 @@ import * as iconLarge from 'img-url:static-build/assets/icon-large.png';
import * as screenshot1 from 'img-url:static-build/assets/screenshot1.png'; import * as screenshot1 from 'img-url:static-build/assets/screenshot1.png';
import * as screenshot2 from 'img-url:static-build/assets/screenshot2.jpg'; import * as screenshot2 from 'img-url:static-build/assets/screenshot2.jpg';
import * as screenshot3 from 'img-url:static-build/assets/screenshot3.jpg'; import * as screenshot3 from 'img-url:static-build/assets/screenshot3.jpg';
import * as screenshot4 from 'img-url:static-build/assets/screenshot4.png';
import * as screenshot5 from 'img-url:static-build/assets/screenshot5.jpg';
import * as screenshot6 from 'img-url:static-build/assets/screenshot6.jpg';
import dedent from 'dedent'; import dedent from 'dedent';
import { lookup as lookupMime } from 'mime-types'; import { lookup as lookupMime } from 'mime-types';
interface Dimensions { const manifestSize = ({ width, height }: { width: number; height: number }) =>
width: number; `${width}x${height}`;
height: number;
}
const manifestSize = ({ width, height }: Dimensions) => `${width}x${height}`;
const formFactor = ({ width, height }: Dimensions) =>
width > height ? 'wide' : 'narrow';
const screenshots = [
screenshot1,
screenshot2,
screenshot3,
screenshot4,
screenshot5,
screenshot6,
].map((screenshot) => ({
src: screenshot.default,
type: lookupMime(screenshot.default),
sizes: manifestSize(screenshot),
form_factor: formFactor(screenshot),
}));
interface Output { interface Output {
[outputPath: string]: string; [outputPath: string]: string;
} }
const toOutput: Output = { const toOutput: Output = {
'index.html': renderPage(<IndexPage />), 'index.html': renderPage(<IndexPage />),
'manifest.json': JSON.stringify({ 'manifest.json': JSON.stringify({
@@ -79,7 +55,23 @@ const toOutput: Output = {
'Compress and compare images with different codecs, right in your browser.', 'Compress and compare images with different codecs, right in your browser.',
lang: 'en', lang: 'en',
categories: ['photo', 'productivity', 'utilities'], categories: ['photo', 'productivity', 'utilities'],
screenshots, screenshots: [
{
src: screenshot1.default,
type: lookupMime(screenshot1.default),
sizes: manifestSize(screenshot1),
},
{
src: screenshot2.default,
type: lookupMime(screenshot2.default),
sizes: manifestSize(screenshot2),
},
{
src: screenshot3.default,
type: lookupMime(screenshot3.default),
sizes: manifestSize(screenshot3),
},
],
share_target: { share_target: {
action: '/?utm_medium=PWA&utm_source=share-target&share-target', action: '/?utm_medium=PWA&utm_source=share-target&share-target',
method: 'POST', method: 'POST',

View File

@@ -16,8 +16,35 @@ import { join as joinPath } from 'path';
import render from 'preact-render-to-string'; import render from 'preact-render-to-string';
import { VNode } from 'preact'; import { VNode } from 'preact';
import { StrictCsp } from 'strict-csp';
export function renderPage(vnode: VNode) { export function renderPage(vnode: VNode) {
return '<!DOCTYPE html>' + render(vnode); const htmlString = '<!DOCTYPE html>' + render(vnode);
const htmlStringWithCsp = addCspAsMetaTag(htmlString);
return htmlStringWithCsp;
}
/**
* Add to the html string a strict Content-Security-Policy (CSP), as a meta tag.
* Details at https://web.dev/strict-csp/
*/
function addCspAsMetaTag(htmlString: string) {
const s = new StrictCsp(htmlString);
// Refactor sourced scripts so that we can set a strict CSP
s.refactorSourcedScriptsForHashBasedCsp();
// Hash inline scripts from this html file, if there are any
const scriptHashes = s.hashAllInlineScripts();
// Generate a strict CSP as a string
// enableUnsafeEval is set to true, to accomodate for uses of eval by emscripten. This makes the CSP a bit less secure
const strictCsp = StrictCsp.getStrictCsp(scriptHashes, {
enableBrowserFallbacks: true,
enableTrustedTypes: false,
enableUnsafeEval: true,
});
// Set this CSP via a meta tag
s.addMetaTag(strictCsp);
const htmlStringWithCsp = s.serializeDom();
return htmlStringWithCsp;
} }
interface OutputMap { interface OutputMap {