diff --git a/packages/integrations/image/package.json b/packages/integrations/image/package.json
index 65e1ecc2c..931a3504f 100644
--- a/packages/integrations/image/package.json
+++ b/packages/integrations/image/package.json
@@ -22,6 +22,7 @@
"exports": {
".": "./dist/index.js",
"./sharp": "./dist/loaders/sharp.js",
+ "./squoosh": "./dist/loaders/squoosh.js",
"./endpoints/dev": "./dist/endpoints/dev.js",
"./endpoints/prod": "./dist/endpoints/prod.js",
"./components": "./components/index.js",
@@ -41,10 +42,10 @@
"test": "mocha --exit --timeout 20000 test"
},
"dependencies": {
+ "@squoosh/lib": "^0.4.0",
"etag": "^1.8.1",
"image-size": "^1.0.1",
"mrmime": "^1.0.0",
- "sharp": "^0.30.6",
"slash": "^4.0.0",
"tiny-glob": "^0.2.9"
},
diff --git a/packages/integrations/image/src/endpoints/dev.ts b/packages/integrations/image/src/endpoints/dev.ts
index dfa7f4900..0924bedcc 100644
--- a/packages/integrations/image/src/endpoints/dev.ts
+++ b/packages/integrations/image/src/endpoints/dev.ts
@@ -1,9 +1,11 @@
import type { APIRoute } from 'astro';
import { lookup } from 'mrmime';
-import loader from '../loaders/sharp.js';
import { loadImage } from '../utils/images.js';
export const get: APIRoute = async ({ request }) => {
+ // @ts-ignore
+ const { default: loader } = await import('virtual:image-loader');
+
try {
const url = new URL(request.url);
const transform = loader.parseTransform(url.searchParams);
diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts
index 28df1ec38..2d6c6f02f 100644
--- a/packages/integrations/image/src/index.ts
+++ b/packages/integrations/image/src/index.ts
@@ -33,7 +33,7 @@ export interface IntegrationOptions {
export default function integration(options: IntegrationOptions = {}): AstroIntegration {
const resolvedOptions = {
- serviceEntryPoint: '@astrojs/image/sharp',
+ serviceEntryPoint: '@astrojs/image/squoosh',
logLevel: 'info' as LoggerLevel,
...options,
};
@@ -48,11 +48,15 @@ export default function integration(options: IntegrationOptions = {}): AstroInte
return {
plugins: [createPlugin(_config, resolvedOptions)],
optimizeDeps: {
- include: ['image-size', 'sharp'],
+ include: [
+ 'image-size',
+ resolvedOptions.serviceEntryPoint === '@astrojs/image/sharp' && 'sharp',
+ resolvedOptions.serviceEntryPoint === '@astrojs/image/squoosh' && '@squoosh/lib',
+ ].filter(Boolean),
},
ssr: {
noExternal: ['@astrojs/image', resolvedOptions.serviceEntryPoint],
- },
+ }
};
}
diff --git a/packages/integrations/image/src/lib/get-image.ts b/packages/integrations/image/src/lib/get-image.ts
index e2fabda55..1b88eba0d 100644
--- a/packages/integrations/image/src/lib/get-image.ts
+++ b/packages/integrations/image/src/lib/get-image.ts
@@ -2,7 +2,6 @@
import slash from 'slash';
import { ROUTE_PATTERN } from '../constants.js';
import { ImageService, isSSRService, OutputFormat, TransformOptions } from '../loaders/index.js';
-import sharp from '../loaders/sharp.js';
import { isRemoteImage, parseAspectRatio } from '../utils/images.js';
import { ImageMetadata } from '../vite-plugin-astro-image.js';
@@ -126,15 +125,9 @@ export async function getImage(
const isDev = import.meta.env?.DEV;
const isLocalImage = !isRemoteImage(resolved.src);
- const _loader = isDev && isLocalImage ? sharp : loader;
-
- if (!_loader) {
- throw new Error('@astrojs/image: loader not found!');
- }
-
// For SSR services, build URLs for the injected route
- if (isSSRService(_loader)) {
- const { searchParams } = _loader.serializeTransform(resolved);
+ if (isSSRService(loader)) {
+ const { searchParams } = loader.serializeTransform(resolved);
// cache all images rendered to HTML
if (globalThis.astroImage?.addStaticImage) {
diff --git a/packages/integrations/image/src/loaders/index.ts b/packages/integrations/image/src/loaders/index.ts
index 7681f25d4..2593531a5 100644
--- a/packages/integrations/image/src/loaders/index.ts
+++ b/packages/integrations/image/src/loaders/index.ts
@@ -1,4 +1,8 @@
///
+
+import { isAspectRatioString, isOutputFormat } from "../utils/images.js";
+
+
export type InputFormat =
| 'heic'
| 'heif'
@@ -10,7 +14,7 @@ export type InputFormat =
| 'webp'
| 'gif';
-export type OutputFormat = 'avif' | 'jpeg' | 'png' | 'webp';
+export type OutputFormat = 'avif' | 'jpeg' | 'jpg' | 'png' | 'webp';
/**
* Defines the original image and transforms that need to be applied to it.
@@ -103,3 +107,85 @@ export function isHostedService(service: ImageService): service is ImageService
export function isSSRService(service: ImageService): service is SSRImageService {
return 'transform' in service;
}
+
+export abstract class BaseSSRService implements SSRImageService {
+ async getImageAttributes(transform: TransformOptions) {
+ // strip off the known attributes
+ const { width, height, src, format, quality, aspectRatio, ...rest } = transform;
+
+ return {
+ ...rest,
+ width: width,
+ height: height,
+ };
+ }
+
+ serializeTransform(transform: TransformOptions) {
+ const searchParams = new URLSearchParams();
+
+ if (transform.quality) {
+ searchParams.append('q', transform.quality.toString());
+ }
+
+ if (transform.format) {
+ searchParams.append('f', transform.format);
+ }
+
+ if (transform.width) {
+ searchParams.append('w', transform.width.toString());
+ }
+
+ if (transform.height) {
+ searchParams.append('h', transform.height.toString());
+ }
+
+ if (transform.aspectRatio) {
+ searchParams.append('ar', transform.aspectRatio.toString());
+ }
+
+ searchParams.append('href', transform.src);
+
+ return { searchParams };
+ }
+
+ parseTransform(searchParams: URLSearchParams) {
+ if (!searchParams.has('href')) {
+ return undefined;
+ }
+
+ let transform: TransformOptions = { src: searchParams.get('href')! };
+
+ if (searchParams.has('q')) {
+ transform.quality = parseInt(searchParams.get('q')!);
+ }
+
+ if (searchParams.has('f')) {
+ const format = searchParams.get('f')!;
+ if (isOutputFormat(format)) {
+ transform.format = format;
+ }
+ }
+
+ if (searchParams.has('w')) {
+ transform.width = parseInt(searchParams.get('w')!);
+ }
+
+ if (searchParams.has('h')) {
+ transform.height = parseInt(searchParams.get('h')!);
+ }
+
+ if (searchParams.has('ar')) {
+ const ratio = searchParams.get('ar')!;
+
+ if (isAspectRatioString(ratio)) {
+ transform.aspectRatio = ratio;
+ } else {
+ transform.aspectRatio = parseFloat(ratio);
+ }
+ }
+
+ return transform;
+ }
+
+ abstract transform(inputBuffer: Buffer, transform: TransformOptions): Promise<{ data: Buffer, format: OutputFormat }>;
+}
diff --git a/packages/integrations/image/src/loaders/sharp.ts b/packages/integrations/image/src/loaders/sharp.ts
index 2368e43d1..1ff35b368 100644
--- a/packages/integrations/image/src/loaders/sharp.ts
+++ b/packages/integrations/image/src/loaders/sharp.ts
@@ -1,86 +1,9 @@
import sharp from 'sharp';
import { isAspectRatioString, isOutputFormat } from '../utils/images.js';
+import { BaseSSRService } from './index.js';
import type { OutputFormat, SSRImageService, TransformOptions } from './index.js';
-class SharpService implements SSRImageService {
- async getImageAttributes(transform: TransformOptions) {
- // strip off the known attributes
- const { width, height, src, format, quality, aspectRatio, ...rest } = transform;
-
- return {
- ...rest,
- width: width,
- height: height,
- };
- }
-
- serializeTransform(transform: TransformOptions) {
- const searchParams = new URLSearchParams();
-
- if (transform.quality) {
- searchParams.append('q', transform.quality.toString());
- }
-
- if (transform.format) {
- searchParams.append('f', transform.format);
- }
-
- if (transform.width) {
- searchParams.append('w', transform.width.toString());
- }
-
- if (transform.height) {
- searchParams.append('h', transform.height.toString());
- }
-
- if (transform.aspectRatio) {
- searchParams.append('ar', transform.aspectRatio.toString());
- }
-
- searchParams.append('href', transform.src);
-
- return { searchParams };
- }
-
- parseTransform(searchParams: URLSearchParams) {
- if (!searchParams.has('href')) {
- return undefined;
- }
-
- let transform: TransformOptions = { src: searchParams.get('href')! };
-
- if (searchParams.has('q')) {
- transform.quality = parseInt(searchParams.get('q')!);
- }
-
- if (searchParams.has('f')) {
- const format = searchParams.get('f')!;
- if (isOutputFormat(format)) {
- transform.format = format;
- }
- }
-
- if (searchParams.has('w')) {
- transform.width = parseInt(searchParams.get('w')!);
- }
-
- if (searchParams.has('h')) {
- transform.height = parseInt(searchParams.get('h')!);
- }
-
- if (searchParams.has('ar')) {
- const ratio = searchParams.get('ar')!;
-
- if (isAspectRatioString(ratio)) {
- transform.aspectRatio = ratio;
- } else {
- transform.aspectRatio = parseFloat(ratio);
- }
- }
-
- return transform;
- }
-
+class SharpService extends BaseSSRService {
async transform(inputBuffer: Buffer, transform: TransformOptions) {
const sharpImage = sharp(inputBuffer, { failOnError: false, pages: -1 });
diff --git a/packages/integrations/image/src/loaders/squoosh.ts b/packages/integrations/image/src/loaders/squoosh.ts
new file mode 100644
index 000000000..29654c978
--- /dev/null
+++ b/packages/integrations/image/src/loaders/squoosh.ts
@@ -0,0 +1,130 @@
+// @ts-ignore
+import { ImagePool } from '@squoosh/lib';
+import { red } from 'kleur/colors';
+import { BaseSSRService } from './index.js';
+import { isAspectRatioString, isOutputFormat } from '../utils/images.js';
+import { error } from '../utils/logger.js';
+import { metadata } from '../utils/metadata.js';
+import type { OutputFormat, SSRImageService, TransformOptions } from './index.js';
+
+class SquooshService extends BaseSSRService {
+ /**
+ * Squoosh doesn't support multithreading when transforming to AVIF files.
+ *
+ * https://github.com/GoogleChromeLabs/squoosh/issues/1111
+ */
+ #imagePool = new ImagePool(1);
+
+ async processAvif(image: any, transform: TransformOptions) {
+ const encodeOptions = transform.quality
+ ? { avif: { quality: transform.quality } }
+ : { avif: {} };
+ await image.encode(encodeOptions);
+ const data = await image.encodedWith.avif;
+
+ return {
+ data: data.binary,
+ format: 'avif' as OutputFormat,
+ };
+ }
+
+ async processJpeg(image: any, transform: TransformOptions) {
+ const encodeOptions = transform.quality
+ ? { mozjpeg: { quality: transform.quality } }
+ : { mozjpeg: {} };
+ await image.encode(encodeOptions);
+ const data = await image.encodedWith.mozjpeg;
+
+ return {
+ data: data.binary,
+ format: 'jpeg' as OutputFormat,
+ };
+ }
+
+ async processPng(image: any, transform: TransformOptions) {
+ await image.encode({ oxipng: {} });
+ const data = await image.encodedWith.oxipng;
+
+ return {
+ data: data.binary,
+ format: 'png' as OutputFormat,
+ };
+ }
+
+ async processWebp(image: any, transform: TransformOptions) {
+ const encodeOptions = transform.quality
+ ? { webp: { quality: transform.quality } }
+ : { webp: {} };
+ await image.encode(encodeOptions);
+ const data = await image.encodedWith.webp;
+
+ return {
+ data: data.binary,
+ format: 'png' as OutputFormat,
+ };
+ }
+
+ async autorotate(image: any, transform: TransformOptions, inputBuffer: Buffer) {
+ // check EXIF orientation data and rotate the image if needed
+ const meta = await metadata(transform.src, inputBuffer);
+
+ switch (meta?.orientation) {
+ case 3:
+ case 4:
+ await image.preprocess({ rotate: { numRotations: 2 } });
+ break;
+ case 5:
+ case 6:
+ await image.preprocess({ rotate: { numRotations: 1 } });
+ break;
+ case 7:
+ case 8:
+ await image.preprocess({ rotate: { numRotations: 3 } });
+ break;
+ }
+ }
+
+ async transform(inputBuffer: Buffer, transform: TransformOptions) {
+ const image = this.#imagePool.ingestImage(inputBuffer);
+
+ let preprocessOptions: any = {};
+
+ // Image files lie! Rotate the image based on EXIF data
+ await this.autorotate(image, transform, inputBuffer);
+
+ if (transform.width || transform.height) {
+ const width = transform.width && Math.round(transform.width);
+ const height = transform.height && Math.round(transform.height);
+
+ preprocessOptions.resize = {
+ width,
+ height,
+ };
+
+ await image.preprocess({ resize: { width, height } });
+ }
+
+ switch (transform.format) {
+ case 'avif':
+ return await this.processAvif(image, transform);
+ case 'jpg':
+ case 'jpeg':
+ return await this.processJpeg(image, transform);
+ case 'png':
+ return await this.processPng(image, transform);
+ case 'webp':
+ return await this.processWebp(image, transform);
+ default:
+ error({
+ level: 'info',
+ prefix: false,
+ message: red(`Unknown image output: "${transform.format}" used for ${transform.src}`),
+ });
+ throw new Error(`Unknown image output: "${transform.format}" used for ${transform.src}`);
+ }
+ }
+}
+
+const service = new SquooshService();
+
+export default service;
diff --git a/packages/integrations/image/src/utils/images.ts b/packages/integrations/image/src/utils/images.ts
index cc5a26cdc..5961a1c99 100644
--- a/packages/integrations/image/src/utils/images.ts
+++ b/packages/integrations/image/src/utils/images.ts
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
import type { OutputFormat, TransformOptions } from '../loaders/index.js';
export function isOutputFormat(value: string): value is OutputFormat {
- return ['avif', 'jpeg', 'png', 'webp'].includes(value);
+ return ['avif', 'jpeg', 'jpg', 'png', 'webp'].includes(value);
}
export function isAspectRatioString(value: string): value is `${number}:${number}` {
diff --git a/packages/integrations/image/src/utils/metadata.ts b/packages/integrations/image/src/utils/metadata.ts
index 349a37535..df3be38a3 100644
--- a/packages/integrations/image/src/utils/metadata.ts
+++ b/packages/integrations/image/src/utils/metadata.ts
@@ -3,8 +3,12 @@ import fs from 'node:fs/promises';
import { InputFormat } from '../loaders/index.js';
import { ImageMetadata } from '../vite-plugin-astro-image.js';
-export async function metadata(src: string): Promise {
- const file = await fs.readFile(src);
+export interface Metadata extends ImageMetadata {
+ orientation?: number;
+}
+
+export async function metadata(src: string, data?: Buffer): Promise {
+ const file = data || await fs.readFile(src);
const { width, height, type, orientation } = await sizeOf(file);
const isPortrait = (orientation || 0) >= 5;
@@ -18,5 +22,6 @@ export async function metadata(src: string): Promise
width: isPortrait ? height : width,
height: isPortrait ? width : height,
format: type as InputFormat,
+ orientation,
};
}
diff --git a/packages/integrations/image/test/rotation.test.js b/packages/integrations/image/test/rotation.test.js
index 19333e572..d0b1211f7 100644
--- a/packages/integrations/image/test/rotation.test.js
+++ b/packages/integrations/image/test/rotation.test.js
@@ -15,7 +15,7 @@ describe('Image rotation', function () {
const url = new URL('./fixtures/rotation/dist/' + pathname, import.meta.url);
const dist = fileURLToPath(url);
const result = sizeOf(dist);
- expect(result).to.deep.equal(expected);
+ expect(result, pathname).to.deep.equal(expected);
}
describe('build', () => {
diff --git a/packages/integrations/image/test/sharp.test.js b/packages/integrations/image/test/sharp.test.js
index 82e332e3d..fc7b22995 100644
--- a/packages/integrations/image/test/sharp.test.js
+++ b/packages/integrations/image/test/sharp.test.js
@@ -1,7 +1,9 @@
import { expect } from 'chai';
-import sharp from '../dist/loaders/sharp.js';
+import { BaseSSRService } from '../dist/loaders/index.js';
+
+describe('abstract SSR service', () => {
+ const service = new BaseSSRService();
-describe('Sharp service', () => {
describe('serializeTransform', () => {
const src = '/assets/image.png';
@@ -16,7 +18,7 @@ describe('Sharp service', () => {
['aspect ratio float', { src, aspectRatio: 1.7 }],
].forEach(([description, props]) => {
it(description, async () => {
- const { searchParams } = await sharp.serializeTransform(props);
+ const { searchParams } = await service.serializeTransform(props);
function verifyProp(expected, search) {
if (expected) {
@@ -52,7 +54,7 @@ describe('Sharp service', () => {
].forEach(([description, params, expected]) => {
it(description, async () => {
const searchParams = new URLSearchParams(params);
- const props = sharp.parseTransform(searchParams);
+ const props = service.parseTransform(searchParams);
expect(props).to.deep.equal(expected);
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c19dc7e09..36ef5d0c5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2192,6 +2192,7 @@ importers:
packages/integrations/image:
specifiers:
+ '@squoosh/lib': ^0.4.0
'@types/etag': ^1.8.1
'@types/sharp': ^0.30.4
astro: workspace:*
@@ -2200,14 +2201,13 @@ importers:
image-size: ^1.0.1
kleur: ^4.1.4
mrmime: ^1.0.0
- sharp: ^0.30.6
slash: ^4.0.0
tiny-glob: ^0.2.9
dependencies:
+ '@squoosh/lib': 0.4.0
etag: 1.8.1
image-size: 1.0.2
mrmime: 1.0.1
- sharp: 0.30.7
slash: 4.0.0
tiny-glob: 0.2.9
devDependencies:
@@ -2242,10 +2242,13 @@ importers:
'@astrojs/image': workspace:*
'@astrojs/node': workspace:*
astro: workspace:*
+ sharp: ^0.30.7
dependencies:
'@astrojs/image': link:../../..
'@astrojs/node': link:../../../../node
astro: link:../../../../../astro
+ devDependencies:
+ sharp: 0.30.7
packages/integrations/lit:
specifiers:
@@ -8440,6 +8443,14 @@ packages:
- react-dom
dev: false
+ /@squoosh/lib/0.4.0:
+ resolution: {integrity: sha512-O1LyugWLZjMI4JZeZMA5vzfhfPjfMZXH5/HmVkRagP8B70wH3uoR7tjxfGNdSavey357MwL8YJDxbGwBBdHp7Q==}
+ engines: {node: ' ^12.5.0 || ^14.0.0 || ^16.0.0 '}
+ dependencies:
+ wasm-feature-detect: 1.2.11
+ web-streams-polyfill: 3.2.1
+ dev: false
+
/@surma/rollup-plugin-off-main-thread/2.2.3:
resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
dependencies:
@@ -9627,6 +9638,7 @@ packages:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.0
+ dev: true
/bl/5.0.0:
resolution: {integrity: sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==}
@@ -9708,6 +9720,7 @@ packages:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
+ dev: true
/buffer/6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@@ -9976,6 +9989,7 @@ packages:
dependencies:
color-name: 1.1.4
simple-swizzle: 0.2.2
+ dev: true
/color-support/1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
@@ -9988,6 +10002,7 @@ packages:
dependencies:
color-convert: 2.0.1
color-string: 1.9.1
+ dev: true
/colorette/2.0.19:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
@@ -10262,6 +10277,7 @@ packages:
engines: {node: '>=10'}
dependencies:
mimic-response: 3.1.0
+ dev: true
/dedent-js/1.0.1:
resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==}
@@ -10513,6 +10529,7 @@ packages:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
once: 1.4.0
+ dev: true
/enquirer/2.3.6:
resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
@@ -11259,6 +11276,7 @@ packages:
/expand-template/2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
+ dev: true
/extend-shallow/2.0.1:
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
@@ -11440,6 +11458,7 @@ packages:
/fs-constants/1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+ dev: true
/fs-extra/7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
@@ -11601,7 +11620,8 @@ packages:
dev: true
/github-from-package/0.0.0:
- resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+ resolution: {integrity: sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=}
+ dev: true
/github-slugger/1.4.0:
resolution: {integrity: sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==}
@@ -12132,6 +12152,7 @@ packages:
/is-arrayish/0.3.2:
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+ dev: true
/is-bigint/1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
@@ -13355,6 +13376,7 @@ packages:
/mimic-response/3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
+ dev: true
/min-indent/1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
@@ -13466,6 +13488,7 @@ packages:
/mkdirp-classic/0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+ dev: true
/mkdirp/0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
@@ -13547,6 +13570,7 @@ packages:
/napi-build-utils/1.0.2:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
+ dev: true
/natural-compare/1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -13596,9 +13620,11 @@ packages:
engines: {node: '>=10'}
dependencies:
semver: 7.3.7
+ dev: true
/node-addon-api/5.0.0:
resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==}
+ dev: true
/node-domexception/1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
@@ -14564,6 +14590,7 @@ packages:
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
+ dev: true
/preferred-pm/3.0.3:
resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==}
@@ -14684,6 +14711,7 @@ packages:
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
+ dev: true
/punycode/2.1.1:
resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
@@ -15357,6 +15385,7 @@ packages:
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
+ dev: true
/shebang-command/1.2.0:
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
@@ -15419,6 +15448,7 @@ packages:
/simple-concat/1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+ dev: true
/simple-get/4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
@@ -15426,11 +15456,13 @@ packages:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
+ dev: true
/simple-swizzle/0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
dependencies:
is-arrayish: 0.3.2
+ dev: true
/sirv/1.0.19:
resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==}
@@ -15934,6 +15966,7 @@ packages:
mkdirp-classic: 0.5.3
pump: 3.0.0
tar-stream: 2.2.0
+ dev: true
/tar-stream/2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
@@ -15944,6 +15977,7 @@ packages:
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.0
+ dev: true
/tar/4.4.19:
resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==}
@@ -16158,6 +16192,7 @@ packages:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
safe-buffer: 5.2.1
+ dev: true
/turbo-darwin-64/1.2.5:
resolution: {integrity: sha512-AjMEF8hlA9vy1gXLHBruqgO42s0M0rKKZLQPM239wli5lKEprmxd8WMSjd9YmxRflS+/fwrXfjVl0QRhHjDIww==}
@@ -16846,6 +16881,10 @@ packages:
'@vue/server-renderer': 3.2.37_vue@3.2.37
'@vue/shared': 3.2.37
+ /wasm-feature-detect/1.2.11:
+ resolution: {integrity: sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w==}
+ dev: false
+
/wcwidth/1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
dependencies: