├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitignore ├── .vscode └── c_cpp_properties.json ├── native └── binding.gyp ├── package.json ├── readme.md ├── src ├── index.ts ├── modules.d.ts ├── null-client.c ├── null-server.c ├── zstd-proxy-posix.c ├── zstd-proxy-posix.h ├── zstd-proxy-uring.c ├── zstd-proxy-uring.h ├── zstd-proxy-utils.h ├── zstd-proxy.addon.cc ├── zstd-proxy.bin.js ├── zstd-proxy.c ├── zstd-proxy.cli.ts ├── zstd-proxy.h ├── zstd-proxy.test.ts └── zstd-proxy.ts ├── tsconfig.json └── yarn.lock /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 2 | 3 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \ 4 | apt-get -y install build-essential socat htop xxd colordiff 5 | 6 | RUN curl -L https://github.com/facebook/zstd/releases/download/v1.5.2/zstd-1.5.2.tar.gz | tar xvz && \ 7 | mv /zstd-* /zstd && \ 8 | cd /zstd && \ 9 | make -j4 && \ 10 | make -j4 install 11 | 12 | RUN git clone https://github.com/axboe/liburing.git /liburing && \ 13 | cd /liburing && \ 14 | ./configure && \ 15 | make -j4 && \ 16 | make -j4 install 17 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Linux", 3 | "build": { "dockerfile": "Dockerfile" }, 4 | "extensions": ["ms-vscode.cpptools-extension-pack"], 5 | "runArgs": ["--ulimit", "memlock=-1:-1"] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .vscode/settings.json 4 | 5 | /build/ 6 | /native/build 7 | /node_modules/ 8 | 9 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "configurations": [ 4 | { 5 | "name": "Linux", 6 | "includePath": [ 7 | "node_modules/nan", 8 | "/usr/include/node", 9 | "/usr/local/include/node" 10 | ] 11 | }, 12 | { 13 | "name": "macOS", 14 | "includePath": [ 15 | "node_modules/nan", 16 | "/usr/local/include/node", 17 | "/opt/homebrew/include" 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /native/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "zstd_proxy", 5 | "libraries": ["-lzstd"], 6 | "include_dirs" : [" { 30 | const client: Socket = connect(9002) 31 | .on('connect', () => zstdProxy({ compress: server, to: client })) 32 | }) 33 | .listen(9001) 34 | ``` 35 | - Create a server on port `9002`, compress `9003` to `9002` and decompress `9002` to `9003` 36 | ```ts 37 | import { zstdProxy } from 'zstd-proxy' 38 | 39 | createServer({ pauseOnConnect: true }) 40 | .on('connection', server => { 41 | const client: Socket = connect(9003) 42 | .on('connect', () => zstdProxy({ compress: client, to: server })) 43 | }) 44 | .listen(9002) 45 | ``` 46 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export {zstdProxy} from './zstd-proxy' 2 | export {zstdProxyCli} from './zstd-proxy.cli' 3 | -------------------------------------------------------------------------------- /src/modules.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.node' 2 | -------------------------------------------------------------------------------- /src/null-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() { 12 | int fd; 13 | struct sockaddr_un addr = { 14 | .sun_family = AF_UNIX, 15 | .sun_path = "connect.sock", 16 | }; 17 | int ret; 18 | int ok = 1; 19 | int len; 20 | 21 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { 22 | perror("socket"); 23 | ok = 0; 24 | } 25 | 26 | if (ok) { 27 | if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 28 | perror("connect"); 29 | ok = 0; 30 | } 31 | } 32 | 33 | size_t total_gb = 32; 34 | size_t total_bytes = total_gb * 1024 * 1024 * 1024; 35 | size_t total_sent_bytes = 0; 36 | size_t buffer_size = 512 * 1024 * 1024; 37 | char *buffer = malloc(buffer_size); 38 | struct timespec start, end; 39 | 40 | for(size_t i = 0; i < buffer_size; i++) buffer[i] = 0; 41 | 42 | FILE *source_file = fopen("/tmp/null-client.data", "w+"); 43 | 44 | if (source_file == NULL) { 45 | perror("open"); 46 | 47 | return 1; 48 | } 49 | 50 | int source_fd = fileno(source_file); 51 | int error = ftruncate(source_fd, buffer_size); 52 | 53 | if (error != 0) { 54 | perror("truncate"); 55 | 56 | return 1; 57 | } 58 | 59 | printf("warming up kernel cache\n"); 60 | 61 | while (total_sent_bytes < (buffer_size * 8)) { 62 | off_t offset = 0; 63 | 64 | if ((len = sendfile(fd, source_fd, &offset, buffer_size)) < 0) { 65 | perror("send"); 66 | ok = 0; 67 | } 68 | 69 | total_sent_bytes += len; 70 | } 71 | 72 | total_sent_bytes = 0; 73 | 74 | printf("starting transmission\n"); 75 | 76 | clock_gettime(CLOCK_REALTIME, &start); 77 | 78 | while (total_sent_bytes < total_bytes) { 79 | off_t offset = 0; 80 | 81 | if ((len = sendfile(fd, source_fd, &offset, buffer_size)) < 0) { 82 | perror("send"); 83 | ok = 0; 84 | } 85 | 86 | total_sent_bytes += len; 87 | } 88 | 89 | if (ok) { 90 | clock_gettime(CLOCK_REALTIME, &end); 91 | 92 | double total_sent_gb = total_sent_bytes / (1024 * 1024 * 1024); 93 | double ns = (end.tv_sec - start.tv_sec) * 1000000000 + (end.tv_nsec - start.tv_nsec); 94 | double seconds = ns / 1000000000; 95 | double gb_per_seconds = total_sent_gb / seconds; 96 | 97 | printf("%.2f GB/s %.2f sent in %.2f seconds\n", gb_per_seconds, total_sent_gb, seconds); 98 | } 99 | 100 | if (fd >= 0) { 101 | close(fd); 102 | } 103 | 104 | return 0; 105 | } -------------------------------------------------------------------------------- /src/null-server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() { 12 | int server_fd, socket_fd; 13 | int opt = 1; 14 | struct sockaddr_un address = { 15 | .sun_family = AF_UNIX, 16 | .sun_path = "connect.sock" 17 | }; 18 | int addrlen = sizeof(address); 19 | 20 | if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { 21 | perror("socket failed"); 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { 26 | perror("setsockopt"); 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { 31 | perror("bind failed"); 32 | exit(EXIT_FAILURE); 33 | } 34 | if (listen(server_fd, 3) < 0) { 35 | perror("listen"); 36 | exit(EXIT_FAILURE); 37 | } 38 | 39 | while (true) { 40 | if ((socket_fd = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { 41 | perror("accept"); 42 | exit(EXIT_FAILURE); 43 | } 44 | 45 | printf("Connection opened\n"); 46 | 47 | size_t size = 64 * 1024 * 1024; 48 | void *buffer = malloc(size); 49 | 50 | while (true) { 51 | size_t received = read(socket_fd, buffer, size); 52 | 53 | if (received < 0) { 54 | perror("read"); 55 | exit(EXIT_FAILURE); 56 | } 57 | 58 | if (received == 0) { 59 | printf("Connection closed\n"); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | close(socket_fd); 66 | shutdown(server_fd, SHUT_RDWR); 67 | 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /src/zstd-proxy-posix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "zstd-proxy-posix.h" 12 | #include "zstd-proxy-utils.h" 13 | 14 | 15 | int zstd_proxy_posix_process(zstd_proxy_connection* connection, ZSTD_inBuffer *input, ZSTD_outBuffer *output) { 16 | int error = 0; 17 | 18 | while (input->pos < input->size) { 19 | output->pos = 0; 20 | 21 | error = connection->process(connection->process_data, input, output); 22 | 23 | if (error != 0) { 24 | break; 25 | } 26 | 27 | ssize_t sent = send(connection->connect->fd, output->dst, output->pos, 0); 28 | 29 | if (sent < 0) { 30 | error = errno; 31 | log_error("error writing to fd %d: %s", connection->connect->fd, strerror(error)); 32 | 33 | break; 34 | } 35 | } 36 | 37 | return error; 38 | } 39 | 40 | int zstd_proxy_posix_run(zstd_proxy_connection* connection) { 41 | int error = 0; 42 | int recv_fd = connection->listen->fd; 43 | size_t size = connection->options->buffer_size; 44 | ZSTD_inBuffer input = { .src = malloc(size) }; 45 | ZSTD_outBuffer output = { .dst = malloc(size), .size = size }; 46 | 47 | if (input.src == NULL || output.dst == NULL) { 48 | error = errno; 49 | 50 | goto cleanup; 51 | } 52 | 53 | if (connection->listen->data_length > 0) { 54 | const void *buffer = input.src; 55 | 56 | input.pos = 0; 57 | input.src = connection->listen->data; 58 | input.size = connection->listen->data_length; 59 | 60 | error = zstd_proxy_posix_process(connection, &input, &output); 61 | 62 | input.src = buffer; 63 | } 64 | 65 | while (!error && !connection->options->stop) { 66 | ssize_t received = recv(recv_fd, (void *)input.src, size, 0); 67 | 68 | if (received == 0) { 69 | break; 70 | } else if (received < 0) { 71 | error = errno; 72 | log_error("error reading fd %d: %s", recv_fd, strerror(error)); 73 | 74 | break; 75 | } 76 | 77 | input.pos = 0; 78 | input.size = received; 79 | 80 | error = zstd_proxy_posix_process(connection, &input, &output); 81 | } 82 | 83 | cleanup: 84 | 85 | free((void *)input.src); 86 | free((void *)output.dst); 87 | 88 | return error; 89 | } 90 | -------------------------------------------------------------------------------- /src/zstd-proxy-posix.h: -------------------------------------------------------------------------------- 1 | #ifndef zstd_proxy_posix_H 2 | #define zstd_proxy_posix_H 3 | 4 | #include "zstd-proxy.h" 5 | 6 | int zstd_proxy_posix_run(zstd_proxy_connection* connection); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/zstd-proxy-uring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "zstd-proxy-uring.h" 8 | #include "zstd-proxy-utils.h" 9 | 10 | typedef struct zstd_proxy_uring_queue zstd_proxy_uring_queue; 11 | typedef struct zstd_proxy_uring_buffer zstd_proxy_uring_buffer; 12 | 13 | typedef enum { 14 | zstd_proxy_uring_recv_buffer, 15 | zstd_proxy_uring_send_buffer 16 | } zstd_proxy_uring_buffer_type; 17 | 18 | struct zstd_proxy_uring_buffer { 19 | /** Incremental ID to sort submission events. */ 20 | size_t id; 21 | /** Buffer type: recv or send. */ 22 | zstd_proxy_uring_buffer_type type; 23 | /** Size of `buffer`. */ 24 | size_t size; 25 | /** I/O fixed buffer index. */ 26 | size_t index; 27 | /** Start position in `buffer`. */ 28 | size_t offset; 29 | /** Ring buffer data. */ 30 | char *data; 31 | /** Ring buffer data. */ 32 | char *queue_data; 33 | 34 | /** Return code. */ 35 | int result; 36 | /** `true` if `buffer` is being filled by the kernel. */ 37 | bool running; 38 | /** `true` if the `buffer` is not used. */ 39 | bool available; 40 | 41 | /** Reference to the queue that owns this buffer. */ 42 | zstd_proxy_uring_queue *queue; 43 | }; 44 | 45 | struct zstd_proxy_uring_queue { 46 | /** Incremental ID to sort submission events. */ 47 | size_t id; 48 | /** `buffers` size. */ 49 | size_t size; 50 | /** How many items are currently running. */ 51 | size_t running; 52 | /** `buffers[].data` size. */ 53 | size_t buffer_size; 54 | 55 | /** Pointer passed to `process. */ 56 | void *process_data; 57 | /** Function pointer called to transform `input` into `output`. */ 58 | int (*process)(void *process_data, ZSTD_inBuffer *input, ZSTD_outBuffer *output); 59 | 60 | struct io_uring uring; 61 | zstd_proxy_connection *connection; 62 | zstd_proxy_uring_buffer buffers[]; 63 | }; 64 | 65 | #define zstd_proxy_uring_foreach(queue, type) \ 66 | for (zstd_proxy_uring_buffer *buffer = NULL, *buffers = queue->buffers; buffers != NULL; buffers = NULL) \ 67 | for ( \ 68 | size_t \ 69 | queue_size = queue->size, \ 70 | buffer_index = type * queue_size, \ 71 | queue_end = buffer_index + queue_size; \ 72 | buffer = &buffers[buffer_index], buffer_index < queue_end; \ 73 | buffer_index++ \ 74 | ) 75 | 76 | void zstd_proxy_uring_options(zstd_proxy_options *options) { 77 | static bool configured = false; 78 | static bool enabled = false; 79 | static bool zero_copy = false; 80 | static bool fixed_buffers = false; 81 | 82 | // Try to run the probe only once 83 | if (!configured) { 84 | struct io_uring_probe *probe = io_uring_get_probe(); 85 | 86 | if (probe == NULL) { 87 | log_error("failed to get io_uring probe, support disabled"); 88 | } else { 89 | if ( 90 | io_uring_opcode_supported(probe, IORING_OP_READ) && 91 | io_uring_opcode_supported(probe, IORING_OP_WRITE) 92 | ) { 93 | enabled = true; 94 | 95 | if ( 96 | io_uring_opcode_supported(probe, IORING_OP_READ_FIXED) && 97 | io_uring_opcode_supported(probe, IORING_OP_WRITE_FIXED) 98 | ) { 99 | fixed_buffers = true; 100 | 101 | if (io_uring_opcode_supported(probe, IORING_OP_SEND_ZC)) { 102 | zero_copy = true; 103 | } 104 | } 105 | } 106 | 107 | io_uring_free_probe(probe); 108 | } 109 | 110 | configured = true; 111 | } 112 | 113 | if (!enabled) { 114 | log_debug("disabling io_uring after failed probe"); 115 | 116 | options->io_uring.enabled = false; 117 | } else { 118 | if (!zero_copy) { 119 | options->io_uring.zero_copy = false; 120 | } 121 | 122 | if (!fixed_buffers) { 123 | options->io_uring.fixed_buffers = false; 124 | } 125 | } 126 | } 127 | 128 | static inline void zstd_proxy_uring_destroy(zstd_proxy_uring_queue *queue) { 129 | if (queue == NULL) { 130 | return; 131 | } 132 | 133 | io_uring_queue_exit(&queue->uring); 134 | free(queue); 135 | } 136 | 137 | int zstd_proxy_uring_create(zstd_proxy_uring_queue **queue_ptr, zstd_proxy_connection *connection) { 138 | int error = 0; 139 | size_t size = connection->options->io_uring.depth; 140 | size_t depth = size * 2; 141 | size_t buffer_size = connection->options->buffer_size; 142 | size_t ring_size = sizeof(zstd_proxy_uring_queue) + sizeof(zstd_proxy_uring_buffer) * depth; 143 | char *mem = malloc(ring_size + depth * buffer_size); 144 | char *buffer_mem = &mem[ring_size]; 145 | zstd_proxy_uring_queue *queue = (zstd_proxy_uring_queue *)mem; 146 | struct io_uring *uring = &queue->uring; 147 | struct iovec vecs[depth]; 148 | 149 | if (queue == NULL) { 150 | error = errno; 151 | log_error("failed to alloc io_uring memory: %s", strerror(error)); 152 | 153 | goto cleanup; 154 | } 155 | 156 | queue->id = 0; 157 | queue->size = size; 158 | queue->running = 0; 159 | queue->connection = connection; 160 | queue->buffer_size = buffer_size; 161 | 162 | for (size_t i = 0; i < depth; i++) { 163 | char *data = &buffer_mem[i * buffer_size]; 164 | struct iovec *vec = &vecs[i]; 165 | zstd_proxy_uring_buffer *buffer = &queue->buffers[i]; 166 | 167 | buffer->id = 0; 168 | buffer->size = buffer_size; 169 | buffer->data = data; 170 | buffer->queue_data = data; 171 | buffer->type = i < size ? zstd_proxy_uring_recv_buffer : zstd_proxy_uring_send_buffer; 172 | buffer->queue = queue; 173 | buffer->index = i; 174 | buffer->running = false; 175 | buffer->available = true; 176 | 177 | vec->iov_len = buffer_size; 178 | vec->iov_base = buffer->data; 179 | } 180 | 181 | error = io_uring_queue_init(depth, uring, 0); 182 | 183 | if (error != 0) { 184 | error = -error; 185 | log_error("failed to init io_uring queue: %s", strerror(error)); 186 | 187 | goto cleanup; 188 | } 189 | 190 | if (connection->options->io_uring.fixed_buffers) { 191 | error = io_uring_register_buffers(uring, vecs, depth); 192 | 193 | if (error != 0) { 194 | error = -error; 195 | log_error("failed to init io_uring buffers: %s", strerror(error)); 196 | 197 | goto cleanup; 198 | } 199 | } 200 | 201 | cleanup: 202 | 203 | *queue_ptr = queue; 204 | 205 | if (error != 0) { 206 | zstd_proxy_uring_destroy(queue); 207 | } 208 | 209 | return error; 210 | } 211 | 212 | static inline zstd_proxy_uring_buffer *zstd_proxy_uring_get(zstd_proxy_uring_queue *queue, zstd_proxy_uring_buffer_type type) { 213 | // Find the oldest waiting buffer 214 | zstd_proxy_uring_buffer *next = NULL; 215 | 216 | zstd_proxy_uring_foreach(queue, type) { 217 | if (!buffer->available) { 218 | if (next == NULL || next->id > buffer->id) { 219 | next = buffer; 220 | } 221 | } 222 | } 223 | 224 | debug_assert(next == NULL || next->type == type); 225 | 226 | return next; 227 | } 228 | 229 | /** Send a recv request */ 230 | int zstd_proxy_uring_submit_recv(zstd_proxy_uring_queue *queue) { 231 | zstd_proxy_uring_buffer *recv_buffer = NULL; 232 | 233 | zstd_proxy_uring_foreach(queue, zstd_proxy_uring_recv_buffer) { 234 | if (buffer->running) { 235 | return 0; 236 | } else if (recv_buffer == NULL && buffer->available) { 237 | recv_buffer = buffer; 238 | } 239 | } 240 | 241 | // We don't have any memory left to fill 242 | if (recv_buffer == NULL) { 243 | return 0; 244 | } 245 | 246 | struct io_uring *uring = &queue->uring; 247 | struct io_uring_sqe *sqe = io_uring_get_sqe(uring); 248 | 249 | if (sqe == NULL) { 250 | log_error("failed to get uring write sqe"); 251 | 252 | return EIO; 253 | } 254 | 255 | debug_assert(!recv_buffer->running); 256 | 257 | recv_buffer->id = ++queue->id; 258 | recv_buffer->running = true; 259 | recv_buffer->available = false; 260 | recv_buffer->data = recv_buffer->queue_data; 261 | 262 | zstd_proxy_connection *connection = queue->connection; 263 | int fd = connection->listen->fd; 264 | 265 | // log_debug("scheduling recv on fd %d, buffer=%d", fd, recv_buffer->index); 266 | 267 | if (connection->options->io_uring.fixed_buffers) { 268 | io_uring_prep_read_fixed(sqe, fd, recv_buffer->data, queue->buffer_size, 0, recv_buffer->index); 269 | } else { 270 | io_uring_prep_read(sqe, fd, recv_buffer->data, queue->buffer_size, 0); 271 | } 272 | 273 | io_uring_sqe_set_data(sqe, recv_buffer); 274 | 275 | int error = io_uring_submit(uring); 276 | 277 | if (error < 0) { 278 | log_error("failed to submit read for fd %d: %s", fd, strerror(errno)); 279 | 280 | return errno; 281 | } 282 | 283 | queue->running++; 284 | 285 | return 0; 286 | } 287 | 288 | int zstd_proxy_uring_submit_send(zstd_proxy_uring_queue *queue) { 289 | zstd_proxy_uring_buffer *buffer = zstd_proxy_uring_get(queue, zstd_proxy_uring_send_buffer); 290 | 291 | // Don't send anything if a send() is currently running 292 | if (buffer == NULL || buffer->running) { 293 | return 0; 294 | } 295 | 296 | // Enqueue a send() 297 | struct io_uring *uring = &queue->uring; 298 | struct io_uring_sqe *sqe = io_uring_get_sqe(uring); 299 | 300 | if (sqe == NULL) { 301 | log_error("failed to get uring write sqe"); 302 | 303 | return EIO; 304 | } 305 | 306 | buffer->running = true; 307 | buffer->available = false; 308 | 309 | zstd_proxy_connection *connection = queue->connection; 310 | zstd_proxy_io_uring_options *options = &connection->options->io_uring; 311 | int fd = connection->connect->fd; 312 | 313 | // log_debug("scheduling send on fd %d, buffer=%d, size=%d", fd, buffer->index, buffer->size); 314 | 315 | if (options->zero_copy) { 316 | io_uring_prep_send_zc_fixed(sqe, fd, &buffer->data[buffer->offset], buffer->size, 0, 0, buffer->index); 317 | } else if (options->fixed_buffers) { 318 | io_uring_prep_write_fixed(sqe, fd, &buffer->data[buffer->offset], buffer->size, 0, buffer->index); 319 | } else { 320 | io_uring_prep_write(sqe, fd, &buffer->data[buffer->offset], buffer->size, 0); 321 | } 322 | 323 | io_uring_sqe_set_data(sqe, buffer); 324 | 325 | int error = io_uring_submit(uring); 326 | 327 | if (error < 0) { 328 | log_error("failed to submit write on fd %d: %s", fd, strerror(errno)); 329 | 330 | return errno; 331 | } 332 | 333 | return 0; 334 | } 335 | 336 | static inline int zstd_proxy_uring_process(zstd_proxy_uring_buffer *recv_buffer) { 337 | int error = 0; 338 | zstd_proxy_uring_queue *queue = recv_buffer->queue; 339 | ZSTD_inBuffer input = { 340 | .src = recv_buffer->data, 341 | .pos = recv_buffer->offset, 342 | .size = recv_buffer->size, 343 | }; 344 | 345 | // Loop in case the input doesn't fit in the output 346 | while (input.pos < input.size) { 347 | zstd_proxy_uring_buffer *send_buffer = NULL; 348 | 349 | zstd_proxy_uring_foreach(queue, zstd_proxy_uring_send_buffer) { 350 | if (buffer->available) { 351 | send_buffer = buffer; 352 | 353 | break; 354 | } 355 | } 356 | 357 | if (send_buffer == NULL) { 358 | // No send buffer available, save the offset and wait for next cqe 359 | recv_buffer->offset = input.pos; 360 | 361 | return 0; 362 | } 363 | 364 | ZSTD_outBuffer output = { 365 | .dst = send_buffer->data, 366 | .pos = 0, 367 | .size = queue->buffer_size, 368 | }; 369 | 370 | // Pass the data to Zstd 371 | error = queue->process(queue->process_data, &input, &output); 372 | 373 | if (error != 0) { 374 | return error; 375 | } 376 | 377 | queue->running++; 378 | send_buffer->id = ++queue->id; 379 | send_buffer->size = output.pos; 380 | send_buffer->offset = 0; 381 | send_buffer->available = false; 382 | 383 | // Enqueue a send() if none are pending 384 | error = zstd_proxy_uring_submit_send(queue); 385 | 386 | if (error != 0) { 387 | return error; 388 | } 389 | } 390 | 391 | // This buffer can be filled by the kernel now 392 | queue->running--; 393 | recv_buffer->available = true; 394 | 395 | return 0; 396 | } 397 | 398 | static inline int zstd_proxy_uring_complete(zstd_proxy_uring_buffer *buffer) { 399 | int res = buffer->result; 400 | zstd_proxy_uring_queue *queue = buffer->queue; 401 | 402 | if (buffer->type == zstd_proxy_uring_recv_buffer) { 403 | int fd = queue->connection->listen->fd; 404 | 405 | log_debug("received data on fd %d, res=%d", fd, res); 406 | 407 | if (res < 0) { 408 | buffer->size = 0; 409 | buffer->offset = 0; 410 | 411 | log_error("failed read socket on fd %d: %s", fd, strerror(-res)); 412 | 413 | return -res; 414 | } 415 | 416 | buffer->size = res; 417 | buffer->offset = 0; 418 | 419 | return 0; 420 | } else if (buffer->type == zstd_proxy_uring_send_buffer) { 421 | int fd = queue->connection->connect->fd; 422 | 423 | log_debug("sent data on fd %d, res=%d", fd, res); 424 | 425 | if (res < 0) { 426 | log_error("failed write to socket on fd %d: %s", fd, strerror(-res)); 427 | 428 | return -res; 429 | } 430 | 431 | size_t size = res; 432 | 433 | if (size < buffer->size) { 434 | // Only a part of the buffer was sent, send more 435 | buffer->size -= size; 436 | buffer->offset += size; 437 | } else { 438 | // Everything got sent, mark the buffer as available 439 | queue->running--; 440 | buffer->available = true; 441 | } 442 | 443 | return 0; 444 | } else { 445 | log_error("invalid buffer type %d", buffer->type); 446 | 447 | return EINVAL; 448 | } 449 | } 450 | 451 | int zstd_proxy_uring_run(zstd_proxy_connection *connection) { 452 | int error = 0; 453 | zstd_proxy_uring_queue *queue; 454 | 455 | // Create the ring buffer 456 | error = zstd_proxy_uring_create(&queue, connection); 457 | 458 | if (error != 0) { 459 | goto cleanup; 460 | } 461 | 462 | queue->process = connection->process; 463 | queue->process_data = connection->process_data; 464 | 465 | // Send any buffered data if needed 466 | if (connection->listen->data_length > 0) { 467 | zstd_proxy_uring_buffer *buffer = &queue->buffers[0]; 468 | 469 | queue->running++; 470 | buffer->available = false; 471 | buffer->data = connection->listen->data; 472 | buffer->size = connection->listen->data_length; 473 | buffer->offset = 0; 474 | 475 | // Process the recv buffer (pass to Zstd and enqueue send() calls) 476 | error = zstd_proxy_uring_process(buffer); 477 | 478 | if (error != 0) { 479 | goto cleanup; 480 | } 481 | } 482 | 483 | // Send a first recv() 484 | error = zstd_proxy_uring_submit_recv(queue); 485 | 486 | if (error != 0) { 487 | goto cleanup; 488 | } 489 | 490 | struct io_uring *uring = &queue->uring; 491 | struct io_uring_cqe *cqe = NULL; 492 | 493 | // Event loop 494 | while (!connection->options->stop && queue->running > 0) { 495 | // Wait for an event 496 | error = io_uring_wait_cqe(uring, &cqe); 497 | 498 | // Acknowledge it 499 | io_uring_cqe_seen(uring, cqe); 500 | 501 | if (error != 0) { 502 | log_error("failed to wait for cqe: %s", strerror(errno)); 503 | 504 | goto cleanup; 505 | } 506 | 507 | // More events are coming (happens during zero-copy) 508 | if (cqe->flags & IORING_CQE_F_MORE) { 509 | continue; 510 | } 511 | 512 | // Get associated buffer with the event 513 | zstd_proxy_uring_buffer *buffer = io_uring_cqe_get_data(cqe); 514 | 515 | buffer->result = cqe->res; 516 | buffer->running = false; 517 | 518 | // Mark event as completed 519 | error = zstd_proxy_uring_complete(buffer); 520 | 521 | if (error != 0) { 522 | goto cleanup; 523 | } 524 | 525 | // Enqueue I/O requests before locking the CPU 526 | error = zstd_proxy_uring_submit_send(queue); 527 | 528 | if (error != 0) { 529 | goto cleanup; 530 | } 531 | 532 | error = zstd_proxy_uring_submit_recv(queue); 533 | 534 | if (error != 0) { 535 | goto cleanup; 536 | } 537 | 538 | // Get the oldest pending recv buffer 539 | zstd_proxy_uring_buffer *recv_buffer = zstd_proxy_uring_get(queue, zstd_proxy_uring_recv_buffer); 540 | 541 | // Nothing to process, wait for more 542 | if (recv_buffer == NULL || recv_buffer->running) { 543 | continue; 544 | } 545 | 546 | // Connection got closed 547 | if (recv_buffer->size == 0) { 548 | break; 549 | } 550 | 551 | debug_assert(!recv_buffer->running); 552 | 553 | // Process the recv buffer (pass to Zstd and enqueue send() calls) 554 | error = zstd_proxy_uring_process(recv_buffer); 555 | 556 | if (error != 0) { 557 | goto cleanup; 558 | } 559 | 560 | // Enqueue another recv() if possible 561 | error = zstd_proxy_uring_submit_recv(queue); 562 | 563 | if (error != 0) { 564 | goto cleanup; 565 | } 566 | } 567 | 568 | log_debug("stopping, queue items running=%lu", queue->running); 569 | 570 | cleanup: 571 | 572 | zstd_proxy_uring_destroy(queue); 573 | 574 | return error; 575 | } 576 | -------------------------------------------------------------------------------- /src/zstd-proxy-uring.h: -------------------------------------------------------------------------------- 1 | #ifndef zstd_proxy_uring_H 2 | #define zstd_proxy_uring_H 3 | 4 | #include "zstd-proxy.h" 5 | 6 | void zstd_proxy_uring_options(zstd_proxy_options *options); 7 | int zstd_proxy_uring_run(zstd_proxy_connection* connection); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/zstd-proxy-utils.h: -------------------------------------------------------------------------------- 1 | #ifndef zstd_proxy_utils_H 2 | #define zstd_proxy_utils_H 3 | 4 | #include 5 | 6 | #define log_buffer(fmt, buffer, size, ...) \ 7 | do { \ 8 | printf(fmt, ##__VA_ARGS__); \ 9 | for(size_t i = 0; i < size; i++) printf("%02X ", ((char *)buffer)[i]); \ 10 | printf("\n"); \ 11 | } while(0) 12 | 13 | #define log_error(fmt, ...) fprintf(stderr, "error: %s in " __FILE__ ": " fmt "\n", __func__, ##__VA_ARGS__) 14 | 15 | #if DEBUG 16 | #define debug_assert(x) assert(x) 17 | #define log_debug(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) 18 | #else 19 | #define debug_assert(x) 20 | #define log_debug(fmt, ...) 21 | #endif 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/zstd-proxy.addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern "C" { 10 | #include "zstd-proxy.h" 11 | #include "zstd-proxy-utils.h" 12 | } 13 | 14 | namespace zstdProxy { 15 | using v8::Context; 16 | using v8::FunctionCallbackInfo; 17 | using v8::Isolate; 18 | using v8::Local; 19 | using v8::Object; 20 | using v8::Value; 21 | 22 | struct thread_data { 23 | int error; 24 | uv_async_t async; 25 | Nan::AsyncResource async_resource; 26 | Nan::Callback callback; 27 | zstd_proxy proxy; 28 | 29 | thread_data(): async_resource("ZstdProxy") {} 30 | }; 31 | 32 | void HandleAbortSignal(int sig) { 33 | void *array[64]; 34 | size_t size = backtrace(array, 64); 35 | 36 | log_error("Fatal error: Signal %d; %lu frames found:", sig, size); 37 | backtrace_symbols_fd(array, size, STDERR_FILENO); 38 | exit(128 + sig); 39 | } 40 | 41 | void *Start(void *data_ptr) { 42 | auto data = (thread_data *)data_ptr; 43 | 44 | data->error = zstd_proxy_run(&data->proxy); 45 | 46 | uv_async_send(&data->async); 47 | 48 | return nullptr; 49 | } 50 | 51 | static inline Local GetOption(Local context, Local options, const char *name) { 52 | return options-> 53 | Get(context, v8::String::NewFromUtf8(context->GetIsolate(), name).ToLocalChecked()). 54 | ToLocalChecked(); 55 | } 56 | 57 | static inline bool GetBoolOption(Local context, Local options, const char *name, bool defaultValue) { 58 | auto option = GetOption(context, options, name); 59 | 60 | if (option->IsUndefined()) { 61 | return defaultValue; 62 | } else { 63 | return option->BooleanValue(context->GetIsolate()); 64 | } 65 | } 66 | 67 | static inline unsigned GetUnsignedOption(Local context, Local options, const char *name, unsigned defaultValue) { 68 | auto option = GetOption(context, options, name); 69 | 70 | if (option->IsUndefined()) { 71 | return defaultValue; 72 | } else { 73 | return option->NumberValue(context).ToChecked(); 74 | } 75 | } 76 | 77 | #if DEBUG 78 | bool registered = false; 79 | #endif 80 | 81 | void Proxy(const FunctionCallbackInfo &args) { 82 | #if DEBUG 83 | if (!registered) { 84 | registered = true; 85 | 86 | signal(SIGSEGV, HandleAbortSignal); 87 | signal(SIGABRT, HandleAbortSignal); 88 | } 89 | #endif 90 | 91 | Isolate *isolate = args.GetIsolate(); 92 | Local context = isolate->GetCurrentContext(); 93 | auto *data = new thread_data(); 94 | auto async = &data->async; 95 | 96 | zstd_proxy_init(&data->proxy); 97 | 98 | async->data = data; 99 | data->proxy.listen.fd = args[0]->NumberValue(context).ToChecked(); 100 | data->proxy.connect.fd = args[1]->NumberValue(context).ToChecked(); 101 | 102 | if (!args[2]->IsUndefined()) { 103 | data->proxy.listen.data = node::Buffer::Data(args[2]); 104 | data->proxy.listen.data_length = node::Buffer::Length(args[2]); 105 | } 106 | 107 | if (!args[3]->IsUndefined()) { 108 | data->proxy.connect.data = node::Buffer::Data(args[3]); 109 | data->proxy.connect.data_length = node::Buffer::Length(args[3]); 110 | } 111 | 112 | if (!args[4]->IsUndefined()) { 113 | auto options = args[4]->ToObject(context).ToLocalChecked(); 114 | auto zstd = GetBoolOption(context, options, "zstd", true); 115 | auto io_uring = GetBoolOption(context, options, "io_uring", true); 116 | auto buffer_size = GetUnsignedOption(context, options, "buffer_size", 0); 117 | 118 | data->proxy.options.zstd.enabled = zstd; 119 | data->proxy.options.io_uring.enabled = io_uring; 120 | 121 | if (zstd) { 122 | auto level = GetUnsignedOption(context, options, "zstd_level", 1); 123 | 124 | data->proxy.options.zstd.level = level; 125 | } 126 | 127 | if (buffer_size > 0) { 128 | data->proxy.options.buffer_size = buffer_size; 129 | } 130 | 131 | if (io_uring) { 132 | auto depth = GetUnsignedOption(context, options, "io_uring_depth", 0); 133 | auto zero_copy = GetBoolOption(context, options, "io_uring_zero_copy", true); 134 | auto fixed_buffers = GetBoolOption(context, options, "io_uring_fixed_buffers", true); 135 | 136 | data->proxy.options.io_uring.zero_copy = zero_copy; 137 | data->proxy.options.io_uring.fixed_buffers = fixed_buffers; 138 | 139 | if (depth > 0) { 140 | data->proxy.options.io_uring.depth = depth; 141 | } 142 | } 143 | } 144 | 145 | data->callback.Reset(args[5].As()); 146 | 147 | uv_async_init(uv_default_loop(), async, [](uv_async_t *async) { 148 | Isolate *isolate = Isolate::GetCurrent(); 149 | v8::HandleScope scope(isolate); 150 | auto data = (thread_data *)async->data; 151 | 152 | if (data->error == 0) { 153 | data->callback.Call(0, nullptr, &data->async_resource); 154 | } else { 155 | Local argv[] = { v8::Number::New(isolate, data->error) }; 156 | 157 | data->callback.Call(1, argv, &data->async_resource); 158 | } 159 | 160 | uv_close((uv_handle_t *)async, [](uv_handle_t *handle) { 161 | auto data = (thread_data *)handle->data; 162 | 163 | delete data; 164 | }); 165 | }); 166 | 167 | pthread_t thread_id = 0; 168 | 169 | int error = pthread_create(&thread_id, nullptr, Start, data); 170 | 171 | if (error != 0) { 172 | std::cerr << "error creating read thread: " << strerror(errno) << std::endl; 173 | 174 | exit(EXIT_FAILURE); 175 | } 176 | } 177 | 178 | void Initialize(Local exports, v8::Local, void *) { 179 | NODE_SET_METHOD(exports, "proxy", Proxy); 180 | } 181 | 182 | NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) 183 | } 184 | -------------------------------------------------------------------------------- /src/zstd-proxy.bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../build').zstdProxyCli() 4 | -------------------------------------------------------------------------------- /src/zstd-proxy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef VERSION 12 | #define VERSION "dev" 13 | #endif 14 | 15 | #ifndef ENABLE_URING 16 | #define ENABLE_URING 1 17 | #endif 18 | 19 | #ifndef DISABLE_ZSTD 20 | #define DISABLE_ZSTD 0 21 | #endif 22 | 23 | #ifndef __linux__ 24 | #undef ENABLE_URING 25 | #define ENABLE_URING 0 26 | #endif 27 | 28 | #if ENABLE_URING 29 | #include "zstd-proxy-uring.h" 30 | #endif 31 | 32 | #include "zstd-proxy-posix.h" 33 | #include "zstd-proxy-utils.h" 34 | 35 | typedef struct { 36 | zstd_proxy *proxy; 37 | 38 | int compress_error; 39 | int decompress_error; 40 | } zstd_proxy_thread; 41 | 42 | static inline int zstd_proxy_remove_nonblock(int fd) { 43 | int flags = fcntl(fd, F_GETFL, 0); 44 | 45 | if (flags == -1) { 46 | log_error("error getting socket flags on fd %d: %s", fd, strerror(errno)); 47 | 48 | return errno; 49 | } 50 | 51 | if (flags & O_NONBLOCK) { 52 | int error = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); 53 | 54 | if (error == -1) { 55 | log_error("error setting socket flags on fd %d: %s", fd, strerror(errno)); 56 | 57 | return errno; 58 | } 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | static inline int zstd_proxy_prepare(int listen_fd, int connect_fd) { 65 | int error = zstd_proxy_remove_nonblock(listen_fd); 66 | 67 | if (error != 0) { 68 | return error; 69 | } 70 | 71 | error = zstd_proxy_remove_nonblock(connect_fd); 72 | 73 | if (error != 0) { 74 | return error; 75 | } 76 | 77 | return 0; 78 | } 79 | 80 | static inline bool zstd_proxy_is_socket(int fd) { 81 | int type; 82 | socklen_t length = sizeof(type); 83 | 84 | return getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &length) == 0; 85 | } 86 | 87 | static inline int zstd_proxy_platform_run(zstd_proxy_connection* connection) { 88 | #if ENABLE_URING 89 | if (connection->options->io_uring.enabled) { 90 | return zstd_proxy_uring_run(connection); 91 | } 92 | #endif 93 | 94 | return zstd_proxy_posix_run(connection); 95 | } 96 | 97 | int zstd_proxy_io( 98 | zstd_proxy_thread *data, 99 | int (*process)(void *process_data, ZSTD_inBuffer *input, ZSTD_outBuffer *output), 100 | void *process_data, 101 | bool invert 102 | ) { 103 | zstd_proxy *proxy = data->proxy; 104 | zstd_proxy_options *options = &proxy->options; 105 | zstd_proxy_descriptor *listen = invert ? &proxy->connect : &proxy->listen; 106 | zstd_proxy_descriptor *connect = invert ? &proxy->listen : &proxy->connect; 107 | zstd_proxy_connection connection = { 108 | .listen = listen, 109 | .connect = connect, 110 | .options = options, 111 | .process = process, 112 | .process_data = process_data, 113 | }; 114 | 115 | int listen_fd = listen->fd; 116 | int connect_fd = connect->fd; 117 | 118 | if (!zstd_proxy_is_socket(listen_fd)) { 119 | return 0; 120 | } 121 | 122 | int error = zstd_proxy_platform_run(&connection); 123 | 124 | options->stop = true; 125 | 126 | shutdown(listen_fd, SHUT_RDWR); 127 | shutdown(connect_fd, SHUT_RDWR); 128 | 129 | return error; 130 | } 131 | 132 | int zstd_proxy_compress_stream(void *ctx, ZSTD_inBuffer *input, ZSTD_outBuffer *output) { 133 | if (ctx == NULL) { 134 | output->pos = input->pos = input->size; 135 | 136 | memcpy(output->dst, input->src, input->size); 137 | } else { 138 | size_t size = ZSTD_compressStream2(ctx, output, input, ZSTD_e_flush); 139 | 140 | if (ZSTD_isError(size)) { 141 | log_error("error compressing data: %s", ZSTD_getErrorName(size)); 142 | 143 | return size; 144 | } 145 | } 146 | 147 | return 0; 148 | } 149 | 150 | int zstd_proxy_decompress_stream(void *ctx, ZSTD_inBuffer *input, ZSTD_outBuffer *output) { 151 | if (ctx == NULL) { 152 | output->pos = input->pos = input->size; 153 | 154 | memcpy(output->dst, input->src, input->size); 155 | } else { 156 | size_t size = ZSTD_decompressStream(ctx, output, input); 157 | 158 | if (ZSTD_isError(size)) { 159 | log_error("error decompressing data: %s", ZSTD_getErrorName(size)); 160 | 161 | return size; 162 | } 163 | } 164 | 165 | return 0; 166 | } 167 | 168 | void *zstd_proxy_compress_thread(void *data_ptr) { 169 | int error = 0; 170 | zstd_proxy_thread *data = data_ptr; 171 | ZSTD_CCtx *ctx = data->proxy->options.zstd.enabled ? ZSTD_createCCtx() : NULL; 172 | 173 | if (ctx != NULL) { 174 | error = ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, data->proxy->options.zstd.level); 175 | 176 | if (ZSTD_isError(error)) { 177 | log_error("failed to set compression level: %s", ZSTD_getErrorName(error)); 178 | 179 | goto cleanup; 180 | } 181 | } 182 | 183 | error = zstd_proxy_io(data, zstd_proxy_compress_stream, ctx, false); 184 | 185 | cleanup: 186 | 187 | if (ctx != NULL) { 188 | ZSTD_freeCCtx(ctx); 189 | } 190 | 191 | data->compress_error = error; 192 | 193 | return NULL; 194 | } 195 | 196 | void *zstd_proxy_decompress_thread(void *data_ptr) { 197 | int error = 0; 198 | zstd_proxy_thread *data = data_ptr; 199 | ZSTD_DCtx *ctx = data->proxy->options.zstd.enabled ? ZSTD_createDCtx() : NULL; 200 | 201 | error = zstd_proxy_io(data, zstd_proxy_decompress_stream, ctx, true); 202 | 203 | if (ctx != NULL) { 204 | ZSTD_freeDCtx(ctx); 205 | } 206 | 207 | data->decompress_error = error; 208 | 209 | return NULL; 210 | } 211 | 212 | void zstd_proxy_init(zstd_proxy *proxy) { 213 | proxy->listen.fd = -1; 214 | proxy->listen.data = NULL; 215 | proxy->listen.data_length = 0; 216 | 217 | proxy->connect.fd = -1; 218 | proxy->connect.data = NULL; 219 | proxy->connect.data_length = 0; 220 | 221 | proxy->options.stop = false; 222 | proxy->options.buffer_size = 4 * 1024 * 1024; 223 | 224 | proxy->options.zstd.enabled = true; 225 | proxy->options.zstd.level = 1; 226 | 227 | proxy->options.io_uring.enabled = true; 228 | proxy->options.io_uring.depth = 4; 229 | proxy->options.io_uring.zero_copy = true; 230 | proxy->options.io_uring.fixed_buffers = true; 231 | } 232 | 233 | /** Connection thread. */ 234 | int zstd_proxy_run(zstd_proxy *proxy) { 235 | int error = 0; 236 | int listen_fd = proxy->listen.fd; 237 | int connect_fd = proxy->connect.fd; 238 | pthread_t compress_thread_id = 0, decompress_thread_id = 0; 239 | zstd_proxy_thread *thread = malloc(sizeof(zstd_proxy_thread)); 240 | 241 | thread->proxy = proxy; 242 | thread->compress_error = 0; 243 | thread->decompress_error = 0; 244 | 245 | error = zstd_proxy_prepare(listen_fd, connect_fd); 246 | 247 | if (error != 0) { 248 | goto cleanup; 249 | } 250 | 251 | #if ENABLE_URING 252 | if (proxy->options.io_uring.enabled) { 253 | zstd_proxy_uring_options(&proxy->options); 254 | } 255 | #endif 256 | 257 | error = pthread_create(&compress_thread_id, NULL, zstd_proxy_compress_thread, thread); 258 | 259 | if (error != 0) { 260 | log_error("error creating read thread: %s", strerror(error)); 261 | 262 | goto cleanup; 263 | } 264 | 265 | error = pthread_create(&decompress_thread_id, NULL, zstd_proxy_decompress_thread, thread); 266 | 267 | if (error != 0) { 268 | log_error("error creating write thread: %s", strerror(error)); 269 | 270 | goto cleanup; 271 | } 272 | 273 | cleanup: 274 | 275 | if (error != 0) { 276 | shutdown(listen_fd, SHUT_RDWR); 277 | shutdown(connect_fd, SHUT_RDWR); 278 | } else { 279 | if (compress_thread_id != 0) { 280 | error = pthread_join(compress_thread_id, NULL); 281 | 282 | if (error != 0) { 283 | log_error("error running compress thread: %s", strerror(error)); 284 | } 285 | } 286 | 287 | if (decompress_thread_id != 0) { 288 | error = pthread_join(decompress_thread_id, NULL); 289 | 290 | if (error != 0) { 291 | log_error("error running decompress thread: %s", strerror(error)); 292 | } 293 | } 294 | } 295 | 296 | int compress_error = thread->compress_error; 297 | int decompress_error = thread->decompress_error; 298 | 299 | close(listen_fd); 300 | close(connect_fd); 301 | free(thread); 302 | 303 | if (compress_error != 0) { 304 | return compress_error; 305 | } 306 | 307 | if (decompress_error != 0) { 308 | return decompress_error; 309 | } 310 | 311 | return error; 312 | } 313 | -------------------------------------------------------------------------------- /src/zstd-proxy.cli.ts: -------------------------------------------------------------------------------- 1 | import { openSync } from "fs" 2 | import { createConnection, createServer, Socket } from "net" 3 | 4 | import { zstdProxy } from "./zstd-proxy" 5 | 6 | export function zstdProxyCli() { 7 | const args = new Map(process.argv.slice(2).map(string => { 8 | const arg = parseArgument(string) 9 | 10 | if(!arg) { 11 | throw new Error(`Invalid argument: ${string}`) 12 | } 13 | 14 | return [arg.key, arg.value] 15 | })) 16 | 17 | const listen = args.get('listen') 18 | const connect = args.get('connect') 19 | const compress = args.get('compress') 20 | 21 | if(!listen) { 22 | throw new Error('Missing --listen argument') 23 | } 24 | 25 | if(!connect) { 26 | throw new Error('Missing --connect argument') 27 | } 28 | 29 | if(!compress) { 30 | throw new Error('Missing --compress argument') 31 | } 32 | 33 | if(compress !== 'listen' && compress !== 'connect') { 34 | throw new Error(`Invalid --compress argument: "${compress}"`) 35 | } 36 | 37 | const getListenSocket = (cb: (server: number | Socket) => void) => { 38 | switch(listen) { 39 | case 'null': 40 | return cb(openSync('/dev/null', 'r+')) 41 | } 42 | 43 | createServer({pauseOnConnect: true}) 44 | .listen(parseSocketOptions(listen)) 45 | .on('connection', server => cb(server)) 46 | .on('error', error => { 47 | console.error(error) 48 | 49 | process.exit(1) 50 | }) 51 | } 52 | const connectSocket = (cb: (client: number | Socket) => void) => { 53 | switch(connect) { 54 | case 'null': 55 | return cb(openSync('/dev/null', 'r+')) 56 | } 57 | 58 | const client: Socket = createConnection(parseSocketOptions(connect)) 59 | .on('connect', () => cb(client)) 60 | } 61 | 62 | getListenSocket( 63 | server => 64 | connectSocket((client) => { 65 | console.log('Connection opened') 66 | 67 | zstdProxy({ 68 | compress: compress === 'listen' ? server : client, 69 | to: compress === 'listen' ? client : server, 70 | onClose(error) { 71 | if(error) { 72 | console.error(error) 73 | } else { 74 | console.log('Connection closed') 75 | } 76 | } 77 | }) 78 | }) 79 | ) 80 | } 81 | 82 | function parseArgument(argument: string) { 83 | if(!argument.startsWith('--')) { 84 | return null 85 | } 86 | 87 | const [key, value] = argument.slice(2).split('=') 88 | 89 | if(!value) { 90 | return null 91 | } 92 | 93 | if(key !== 'listen' && key !== 'connect' && key !== 'compress') { 94 | return null 95 | } 96 | 97 | return {key, value} 98 | } 99 | 100 | function parseSocketOptions(path: string) { 101 | if(path[0] === '.' || path[1] === '/') { 102 | return {path} 103 | } else { 104 | let [host, portString] = path.split(':') 105 | 106 | if(!portString) { 107 | [portString, host] = [host, portString] 108 | } 109 | 110 | const port = parseInt(portString, 10) 111 | 112 | if(Number.isNaN(port) || port < 0 || port > 65535) { 113 | throw new Error(`Invalid port number: ${portString}`) 114 | } 115 | 116 | return {host, port} 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/zstd-proxy.h: -------------------------------------------------------------------------------- 1 | #ifndef zstd_proxy_H 2 | #define zstd_proxy_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | typedef struct { 10 | int fd; 11 | void* data; 12 | size_t data_length; 13 | } zstd_proxy_descriptor; 14 | 15 | typedef struct { 16 | bool enabled; 17 | 18 | size_t depth; 19 | bool zero_copy; 20 | bool fixed_buffers; 21 | } zstd_proxy_io_uring_options; 22 | 23 | typedef struct { 24 | bool enabled; 25 | 26 | size_t level; 27 | } zstd_proxy_zstd_options; 28 | 29 | typedef struct { 30 | bool stop; 31 | size_t buffer_size; 32 | 33 | zstd_proxy_zstd_options zstd; 34 | zstd_proxy_io_uring_options io_uring; 35 | } zstd_proxy_options; 36 | 37 | typedef struct { 38 | zstd_proxy_options options; 39 | zstd_proxy_descriptor listen; 40 | zstd_proxy_descriptor connect; 41 | } zstd_proxy; 42 | 43 | typedef int (*zstd_proxy_process_callback)(void *process_data, ZSTD_inBuffer *input, ZSTD_outBuffer *output); 44 | 45 | typedef struct { 46 | zstd_proxy_options *options; 47 | zstd_proxy_descriptor *listen; 48 | zstd_proxy_descriptor *connect; 49 | 50 | zstd_proxy_process_callback process; 51 | void *process_data; 52 | } zstd_proxy_connection; 53 | 54 | void zstd_proxy_init(zstd_proxy *proxy); 55 | int zstd_proxy_run(zstd_proxy *proxy); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/zstd-proxy.test.ts: -------------------------------------------------------------------------------- 1 | import { request } from "http"; 2 | import { createServer, createConnection, Server, Socket } from "net"; 3 | import { createServer as createHttpServer } from "http"; 4 | 5 | import { zstdProxy } from "./zstd-proxy"; 6 | 7 | const serverPort = 8540; 8 | const serverProxyPort = 8541; 9 | const clientProxyPort = 8542; 10 | const fail = (error: Error) => { 11 | console.error(error); 12 | 13 | process.exit(1); 14 | }; 15 | 16 | console.log("running test"); 17 | runTest() 18 | .catch((error) => { 19 | console.error(error); 20 | process.exit(1); 21 | }) 22 | .then(() => { 23 | console.log("Test passed"); 24 | }); 25 | 26 | async function runTest() { 27 | let pass = false; 28 | 29 | await testHarness({ 30 | // mode: 'http', 31 | server: { 32 | head: Buffer.from("yo"), 33 | connect(socket) { 34 | // socket.write("yo"); 35 | }, 36 | data(data, socket) { 37 | console.log( 38 | "Message from client: %s, size=%s", 39 | data.toString("utf-8"), 40 | data.length 41 | ); 42 | 43 | switch (data.toString("utf-8")) { 44 | case "yo! what's up?": 45 | return socket.write("just trying to test a proxy"); 46 | case "how it is going?": 47 | return socket.write("you tell me!"); 48 | case "seems to be working fine": 49 | return socket.write("yea I guess you're right"); 50 | case "always am, ciao": 51 | pass = true; 52 | return socket.end(); 53 | default: 54 | return fail(new Error("Invalid client message")); 55 | } 56 | }, 57 | }, 58 | client: { 59 | data(data, socket) { 60 | console.log("Message from server: %s", data.toString("utf-8")); 61 | 62 | switch (data.toString("utf-8")) { 63 | case "yo": 64 | return socket.write("yo! what's up?"); 65 | case "just trying to test a proxy": 66 | return socket.write("how it is going?"); 67 | case "you tell me!": 68 | return socket.write("seems to be working fine"); 69 | case "yea I guess you're right": 70 | return socket.write("always am, ciao"); 71 | default: 72 | return fail(new Error("Invalid server message")); 73 | } 74 | }, 75 | }, 76 | }); 77 | 78 | if (!pass) { 79 | throw new Error("Connection closed"); 80 | } 81 | } 82 | 83 | async function testHarness(options: { 84 | mode?: "socket" | "http"; 85 | server: { 86 | head?: Buffer; 87 | connect?(socket: Socket): void; 88 | data?(data: Buffer, socket: Socket): void; 89 | }; 90 | client: { 91 | connect?(socket: Socket): void; 92 | data?(data: Buffer, socket: Socket): void; 93 | }; 94 | }) { 95 | const server = await listen( 96 | serverPort, 97 | (socket, head) => { 98 | if (head?.length) { 99 | options.server.data?.(head, socket); 100 | } 101 | 102 | socket.on("data", (data) => options.server.data?.(data, socket)); 103 | options.server.connect?.(socket); 104 | }, 105 | { mode: options.mode, pauseOnConnect: false } 106 | ); 107 | 108 | const serverProxy = await listen(serverProxyPort, (client) => { 109 | if (options.mode === "http") { 110 | request(`http://localhost:${serverPort}`, { 111 | headers: { 112 | Connection: "upgrade", 113 | Upgrade: "test-zstd-proxy", 114 | }, 115 | }) 116 | .on("error", fail) 117 | .on("upgrade", (_, socket, head) => { 118 | zstdProxy({ 119 | compress: { socket, head: options.server.head }, 120 | to: { socket: client, head }, 121 | }); 122 | }) 123 | .end(); 124 | } else { 125 | const socket = createConnection(serverPort); 126 | 127 | socket.on("error", fail).on("connect", () => 128 | zstdProxy({ 129 | compress: { socket, head: options.server.head }, 130 | to: client, 131 | }) 132 | ); 133 | } 134 | }); 135 | 136 | const clientProxy = await listen(clientProxyPort, (client) => { 137 | const socket = createConnection(serverProxyPort); 138 | 139 | socket 140 | .on("error", fail) 141 | .on("connect", () => zstdProxy({ compress: client, to: socket })); 142 | }); 143 | 144 | await new Promise((resolve, reject) => { 145 | const socket = createConnection(clientProxyPort); 146 | 147 | socket 148 | .on("error", reject) 149 | .on("close", () => resolve()) 150 | .on("connect", () => options.client.connect?.(socket)) 151 | .on("data", (data) => options.client.data?.(data, socket)); 152 | }); 153 | 154 | server.close(); 155 | serverProxy.close(); 156 | clientProxy.close(); 157 | } 158 | 159 | async function listen( 160 | port: number, 161 | handle: (socket: Socket, head?: Buffer) => void, 162 | { 163 | mode, 164 | pauseOnConnect = true, 165 | }: { mode?: "socket" | "http"; pauseOnConnect?: boolean } = {} 166 | ) { 167 | if (mode === "http") { 168 | return await new Promise((resolve, reject) => { 169 | const server: Server = createHttpServer((_req, res) => 170 | res.writeHead(404).end() 171 | ) 172 | .listen(port) 173 | .on("error", (error) => reject(error)) 174 | .on("listening", () => resolve(server)) 175 | .on("upgrade", ({ socket }, _stream, head) => { 176 | setTimeout(() => { 177 | console.log("server upgrade request"); 178 | socket.write( 179 | Buffer.from( 180 | `HTTP/1.1 101 Handshake\r\n` + 181 | `Upgrade: test-zstd-proxy\r\n` + 182 | "Connection: Upgrade\r\n" + 183 | "\r\n" 184 | ) 185 | ); 186 | 187 | handle(socket, head); 188 | }, 50); 189 | }); 190 | }); 191 | } else { 192 | return await new Promise((resolve, reject) => { 193 | const server: Server = createServer({ pauseOnConnect }, handle) 194 | .listen(port) 195 | .on("error", (error) => reject(error)) 196 | .on("listening", () => resolve(server)); 197 | }); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/zstd-proxy.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "net"; 2 | 3 | import { proxy } from "../native/build/Release/zstd_proxy.node"; 4 | 5 | export type SocketWithHead = { socket: Socket; head?: Buffer }; 6 | export type MaybeSocketWithHead = Socket | SocketWithHead; 7 | 8 | export interface ZstdProxyOptions { 9 | compress: number | MaybeSocketWithHead; 10 | to: number | MaybeSocketWithHead; 11 | 12 | onClose?(error?: Error): void; 13 | 14 | zstd?: { 15 | /** Set to `false` to disable compression */ 16 | enabled?: boolean; 17 | 18 | /** Zstd compression level. Defaults to `1`. */ 19 | level?: number; 20 | }; 21 | 22 | /** 23 | * Linux io_uring specific options. 24 | * More efficient, this implementation will use around `depth * 2 * bufferSize` bytes of memory per connection. 25 | */ 26 | io_uring?: { 27 | /** Set to `false` to disable io_uring. */ 28 | enabled?: boolean; 29 | 30 | /** Configure how many pending items can be queued. Defaults to `8`. */ 31 | depth?: number; 32 | /** Set to `false` to disable zero-copy networking. */ 33 | zeroCopy?: number; 34 | /** Configure the ring buffer size in bytes. Defaults to 1 MB (`1024 * 1024`). */ 35 | bufferSize?: number; 36 | /** Set to `false` to disable fixed buffers. */ 37 | fixedBuffers?: boolean; 38 | }; 39 | } 40 | 41 | export async function zstdProxy(options: ZstdProxyOptions) { 42 | const to = socketWithHead(options.to); 43 | const compress = socketWithHead(options.compress); 44 | 45 | proxy( 46 | compress.fd, 47 | to.fd, 48 | compress.head, 49 | to.head, 50 | { 51 | zstd: options.zstd?.enabled, 52 | zstd_level: options.zstd?.level, 53 | io_uring: options.io_uring?.enabled, 54 | io_uring_depth: options.io_uring?.depth, 55 | io_uring_zero_copy: options.io_uring?.zeroCopy, 56 | io_uring_buffer_size: options.io_uring?.bufferSize, 57 | io_uring_fixed_buffers: options.io_uring?.fixedBuffers, 58 | }, 59 | (code?: number) => { 60 | to.socket?.destroy(); 61 | compress.socket?.destroy(); 62 | 63 | options.onClose?.( 64 | typeof code === "number" ? new Error(`Error ${code}`) : undefined 65 | ); 66 | } 67 | ); 68 | } 69 | 70 | // Prevent Node.js from sending any system calls on the socket file descriptor. 71 | function disown(socket: Socket) { 72 | const anySocket = socket as any; 73 | const { _handle } = anySocket; 74 | const fd = anySocket._handle?.fd; 75 | 76 | if (typeof fd !== "number" || fd < 0) { 77 | throw new Error("Invalid socket file descriptor"); 78 | } 79 | 80 | // Prevent Node.js from calling recv() on the socket 81 | socket.setNoDelay().pause(); 82 | _handle.readStop(); 83 | _handle.readStop = () => {}; 84 | _handle.readStart = () => {}; 85 | anySocket._handle = null; 86 | 87 | return fd; 88 | } 89 | 90 | function socketWithHead(socket: number | MaybeSocketWithHead) { 91 | return typeof socket === "number" 92 | ? { fd: socket } 93 | : socket instanceof Socket 94 | ? { fd: disown(socket), socket } 95 | : { 96 | fd: disown(socket.socket), 97 | socket: socket.socket, 98 | head: socket.head, 99 | }; 100 | } 101 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "lib": ["es2022"], 5 | "strict": true, 6 | "strictNullChecks": true, 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "declaration": true, 10 | "esModuleInterop": true, 11 | "resolveJsonModule": true, 12 | "experimentalDecorators": true, 13 | "emitDecoratorMetadata": true, 14 | "moduleResolution": "node", 15 | "composite": true, 16 | "incremental": true, 17 | "skipLibCheck": true, 18 | "preserveWatchOutput": true, 19 | "rootDir": "src", 20 | "outDir": "build", 21 | "tsBuildInfoFile": "build/tsconfig.tsbuildinfo", 22 | "module": "commonjs", 23 | "types": ["node"] 24 | }, 25 | "include": ["src/**/*.ts"] 26 | } 27 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@gar/promisify@^1.1.3": 6 | version "1.1.3" 7 | resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" 8 | integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== 9 | 10 | "@npmcli/fs@^2.1.0": 11 | version "2.1.2" 12 | resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" 13 | integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== 14 | dependencies: 15 | "@gar/promisify" "^1.1.3" 16 | semver "^7.3.5" 17 | 18 | "@npmcli/move-file@^2.0.0": 19 | version "2.0.1" 20 | resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" 21 | integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== 22 | dependencies: 23 | mkdirp "^1.0.4" 24 | rimraf "^3.0.2" 25 | 26 | "@tootallnate/once@2": 27 | version "2.0.0" 28 | resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" 29 | integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== 30 | 31 | "@types/node@^18.11.0": 32 | version "18.11.2" 33 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.2.tgz#c59b7641832531264fda3f1ba610362dc9a7dfc8" 34 | integrity sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw== 35 | 36 | abbrev@^1.0.0: 37 | version "1.1.1" 38 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 39 | integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 40 | 41 | agent-base@6, agent-base@^6.0.2: 42 | version "6.0.2" 43 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" 44 | integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== 45 | dependencies: 46 | debug "4" 47 | 48 | agentkeepalive@^4.2.1: 49 | version "4.2.1" 50 | resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" 51 | integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== 52 | dependencies: 53 | debug "^4.1.0" 54 | depd "^1.1.2" 55 | humanize-ms "^1.2.1" 56 | 57 | aggregate-error@^3.0.0: 58 | version "3.1.0" 59 | resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" 60 | integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== 61 | dependencies: 62 | clean-stack "^2.0.0" 63 | indent-string "^4.0.0" 64 | 65 | ansi-regex@^5.0.1: 66 | version "5.0.1" 67 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" 68 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 69 | 70 | ansi-styles@^3.2.1: 71 | version "3.2.1" 72 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 73 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 74 | dependencies: 75 | color-convert "^1.9.0" 76 | 77 | "aproba@^1.0.3 || ^2.0.0": 78 | version "2.0.0" 79 | resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" 80 | integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== 81 | 82 | are-we-there-yet@^3.0.0: 83 | version "3.0.1" 84 | resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" 85 | integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== 86 | dependencies: 87 | delegates "^1.0.0" 88 | readable-stream "^3.6.0" 89 | 90 | balanced-match@^1.0.0: 91 | version "1.0.2" 92 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 93 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 94 | 95 | brace-expansion@^1.1.7: 96 | version "1.1.11" 97 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 98 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 99 | dependencies: 100 | balanced-match "^1.0.0" 101 | concat-map "0.0.1" 102 | 103 | brace-expansion@^2.0.1: 104 | version "2.0.1" 105 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" 106 | integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== 107 | dependencies: 108 | balanced-match "^1.0.0" 109 | 110 | cacache@^16.1.0: 111 | version "16.1.3" 112 | resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" 113 | integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== 114 | dependencies: 115 | "@npmcli/fs" "^2.1.0" 116 | "@npmcli/move-file" "^2.0.0" 117 | chownr "^2.0.0" 118 | fs-minipass "^2.1.0" 119 | glob "^8.0.1" 120 | infer-owner "^1.0.4" 121 | lru-cache "^7.7.1" 122 | minipass "^3.1.6" 123 | minipass-collect "^1.0.2" 124 | minipass-flush "^1.0.5" 125 | minipass-pipeline "^1.2.4" 126 | mkdirp "^1.0.4" 127 | p-map "^4.0.0" 128 | promise-inflight "^1.0.1" 129 | rimraf "^3.0.2" 130 | ssri "^9.0.0" 131 | tar "^6.1.11" 132 | unique-filename "^2.0.0" 133 | 134 | call-bind@^1.0.0, call-bind@^1.0.2: 135 | version "1.0.2" 136 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" 137 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== 138 | dependencies: 139 | function-bind "^1.1.1" 140 | get-intrinsic "^1.0.2" 141 | 142 | chalk@^2.4.1: 143 | version "2.4.2" 144 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 145 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 146 | dependencies: 147 | ansi-styles "^3.2.1" 148 | escape-string-regexp "^1.0.5" 149 | supports-color "^5.3.0" 150 | 151 | chownr@^2.0.0: 152 | version "2.0.0" 153 | resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" 154 | integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== 155 | 156 | clean-stack@^2.0.0: 157 | version "2.2.0" 158 | resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" 159 | integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== 160 | 161 | color-convert@^1.9.0: 162 | version "1.9.3" 163 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 164 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 165 | dependencies: 166 | color-name "1.1.3" 167 | 168 | color-name@1.1.3: 169 | version "1.1.3" 170 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 171 | integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== 172 | 173 | color-support@^1.1.3: 174 | version "1.1.3" 175 | resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" 176 | integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== 177 | 178 | concat-map@0.0.1: 179 | version "0.0.1" 180 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 181 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 182 | 183 | console-control-strings@^1.1.0: 184 | version "1.1.0" 185 | resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 186 | integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== 187 | 188 | cross-spawn@^6.0.5: 189 | version "6.0.5" 190 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" 191 | integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== 192 | dependencies: 193 | nice-try "^1.0.4" 194 | path-key "^2.0.1" 195 | semver "^5.5.0" 196 | shebang-command "^1.2.0" 197 | which "^1.2.9" 198 | 199 | debug@4, debug@^4.1.0, debug@^4.3.3: 200 | version "4.3.4" 201 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 202 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 203 | dependencies: 204 | ms "2.1.2" 205 | 206 | define-properties@^1.1.3, define-properties@^1.1.4: 207 | version "1.1.4" 208 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" 209 | integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== 210 | dependencies: 211 | has-property-descriptors "^1.0.0" 212 | object-keys "^1.1.1" 213 | 214 | delegates@^1.0.0: 215 | version "1.0.0" 216 | resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 217 | integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== 218 | 219 | depd@^1.1.2: 220 | version "1.1.2" 221 | resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" 222 | integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== 223 | 224 | emoji-regex@^8.0.0: 225 | version "8.0.0" 226 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 227 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 228 | 229 | encoding@^0.1.13: 230 | version "0.1.13" 231 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" 232 | integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== 233 | dependencies: 234 | iconv-lite "^0.6.2" 235 | 236 | env-paths@^2.2.0: 237 | version "2.2.1" 238 | resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" 239 | integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== 240 | 241 | err-code@^2.0.2: 242 | version "2.0.3" 243 | resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" 244 | integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== 245 | 246 | error-ex@^1.3.1: 247 | version "1.3.2" 248 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" 249 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== 250 | dependencies: 251 | is-arrayish "^0.2.1" 252 | 253 | es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.5: 254 | version "1.20.4" 255 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" 256 | integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA== 257 | dependencies: 258 | call-bind "^1.0.2" 259 | es-to-primitive "^1.2.1" 260 | function-bind "^1.1.1" 261 | function.prototype.name "^1.1.5" 262 | get-intrinsic "^1.1.3" 263 | get-symbol-description "^1.0.0" 264 | has "^1.0.3" 265 | has-property-descriptors "^1.0.0" 266 | has-symbols "^1.0.3" 267 | internal-slot "^1.0.3" 268 | is-callable "^1.2.7" 269 | is-negative-zero "^2.0.2" 270 | is-regex "^1.1.4" 271 | is-shared-array-buffer "^1.0.2" 272 | is-string "^1.0.7" 273 | is-weakref "^1.0.2" 274 | object-inspect "^1.12.2" 275 | object-keys "^1.1.1" 276 | object.assign "^4.1.4" 277 | regexp.prototype.flags "^1.4.3" 278 | safe-regex-test "^1.0.0" 279 | string.prototype.trimend "^1.0.5" 280 | string.prototype.trimstart "^1.0.5" 281 | unbox-primitive "^1.0.2" 282 | 283 | es-to-primitive@^1.2.1: 284 | version "1.2.1" 285 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" 286 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 287 | dependencies: 288 | is-callable "^1.1.4" 289 | is-date-object "^1.0.1" 290 | is-symbol "^1.0.2" 291 | 292 | escape-string-regexp@^1.0.5: 293 | version "1.0.5" 294 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 295 | integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== 296 | 297 | fs-minipass@^2.0.0, fs-minipass@^2.1.0: 298 | version "2.1.0" 299 | resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" 300 | integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== 301 | dependencies: 302 | minipass "^3.0.0" 303 | 304 | fs.realpath@^1.0.0: 305 | version "1.0.0" 306 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 307 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 308 | 309 | function-bind@^1.1.1: 310 | version "1.1.1" 311 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 312 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 313 | 314 | function.prototype.name@^1.1.5: 315 | version "1.1.5" 316 | resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" 317 | integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== 318 | dependencies: 319 | call-bind "^1.0.2" 320 | define-properties "^1.1.3" 321 | es-abstract "^1.19.0" 322 | functions-have-names "^1.2.2" 323 | 324 | functions-have-names@^1.2.2: 325 | version "1.2.3" 326 | resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" 327 | integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== 328 | 329 | gauge@^4.0.3: 330 | version "4.0.4" 331 | resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" 332 | integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== 333 | dependencies: 334 | aproba "^1.0.3 || ^2.0.0" 335 | color-support "^1.1.3" 336 | console-control-strings "^1.1.0" 337 | has-unicode "^2.0.1" 338 | signal-exit "^3.0.7" 339 | string-width "^4.2.3" 340 | strip-ansi "^6.0.1" 341 | wide-align "^1.1.5" 342 | 343 | get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: 344 | version "1.1.3" 345 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" 346 | integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== 347 | dependencies: 348 | function-bind "^1.1.1" 349 | has "^1.0.3" 350 | has-symbols "^1.0.3" 351 | 352 | get-symbol-description@^1.0.0: 353 | version "1.0.0" 354 | resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" 355 | integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== 356 | dependencies: 357 | call-bind "^1.0.2" 358 | get-intrinsic "^1.1.1" 359 | 360 | glob@^7.1.3, glob@^7.1.4: 361 | version "7.2.3" 362 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 363 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 364 | dependencies: 365 | fs.realpath "^1.0.0" 366 | inflight "^1.0.4" 367 | inherits "2" 368 | minimatch "^3.1.1" 369 | once "^1.3.0" 370 | path-is-absolute "^1.0.0" 371 | 372 | glob@^8.0.1: 373 | version "8.0.3" 374 | resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" 375 | integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== 376 | dependencies: 377 | fs.realpath "^1.0.0" 378 | inflight "^1.0.4" 379 | inherits "2" 380 | minimatch "^5.0.1" 381 | once "^1.3.0" 382 | 383 | graceful-fs@^4.1.2, graceful-fs@^4.2.6: 384 | version "4.2.10" 385 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" 386 | integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== 387 | 388 | has-bigints@^1.0.1, has-bigints@^1.0.2: 389 | version "1.0.2" 390 | resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" 391 | integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== 392 | 393 | has-flag@^3.0.0: 394 | version "3.0.0" 395 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 396 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== 397 | 398 | has-property-descriptors@^1.0.0: 399 | version "1.0.0" 400 | resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" 401 | integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== 402 | dependencies: 403 | get-intrinsic "^1.1.1" 404 | 405 | has-symbols@^1.0.2, has-symbols@^1.0.3: 406 | version "1.0.3" 407 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" 408 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 409 | 410 | has-tostringtag@^1.0.0: 411 | version "1.0.0" 412 | resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" 413 | integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== 414 | dependencies: 415 | has-symbols "^1.0.2" 416 | 417 | has-unicode@^2.0.1: 418 | version "2.0.1" 419 | resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 420 | integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== 421 | 422 | has@^1.0.3: 423 | version "1.0.3" 424 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 425 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 426 | dependencies: 427 | function-bind "^1.1.1" 428 | 429 | hosted-git-info@^2.1.4: 430 | version "2.8.9" 431 | resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" 432 | integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== 433 | 434 | http-cache-semantics@^4.1.0: 435 | version "4.1.0" 436 | resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" 437 | integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== 438 | 439 | http-proxy-agent@^5.0.0: 440 | version "5.0.0" 441 | resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" 442 | integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== 443 | dependencies: 444 | "@tootallnate/once" "2" 445 | agent-base "6" 446 | debug "4" 447 | 448 | https-proxy-agent@^5.0.0: 449 | version "5.0.1" 450 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" 451 | integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== 452 | dependencies: 453 | agent-base "6" 454 | debug "4" 455 | 456 | humanize-ms@^1.2.1: 457 | version "1.2.1" 458 | resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" 459 | integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== 460 | dependencies: 461 | ms "^2.0.0" 462 | 463 | iconv-lite@^0.6.2: 464 | version "0.6.3" 465 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" 466 | integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== 467 | dependencies: 468 | safer-buffer ">= 2.1.2 < 3.0.0" 469 | 470 | imurmurhash@^0.1.4: 471 | version "0.1.4" 472 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 473 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 474 | 475 | indent-string@^4.0.0: 476 | version "4.0.0" 477 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" 478 | integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== 479 | 480 | infer-owner@^1.0.4: 481 | version "1.0.4" 482 | resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" 483 | integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== 484 | 485 | inflight@^1.0.4: 486 | version "1.0.6" 487 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 488 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 489 | dependencies: 490 | once "^1.3.0" 491 | wrappy "1" 492 | 493 | inherits@2, inherits@^2.0.3: 494 | version "2.0.4" 495 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 496 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 497 | 498 | internal-slot@^1.0.3: 499 | version "1.0.3" 500 | resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" 501 | integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== 502 | dependencies: 503 | get-intrinsic "^1.1.0" 504 | has "^1.0.3" 505 | side-channel "^1.0.4" 506 | 507 | ip@^2.0.0: 508 | version "2.0.0" 509 | resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" 510 | integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== 511 | 512 | is-arrayish@^0.2.1: 513 | version "0.2.1" 514 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" 515 | integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== 516 | 517 | is-bigint@^1.0.1: 518 | version "1.0.4" 519 | resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" 520 | integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== 521 | dependencies: 522 | has-bigints "^1.0.1" 523 | 524 | is-boolean-object@^1.1.0: 525 | version "1.1.2" 526 | resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" 527 | integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== 528 | dependencies: 529 | call-bind "^1.0.2" 530 | has-tostringtag "^1.0.0" 531 | 532 | is-callable@^1.1.4, is-callable@^1.2.7: 533 | version "1.2.7" 534 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" 535 | integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== 536 | 537 | is-core-module@^2.9.0: 538 | version "2.11.0" 539 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 540 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== 541 | dependencies: 542 | has "^1.0.3" 543 | 544 | is-date-object@^1.0.1: 545 | version "1.0.5" 546 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" 547 | integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== 548 | dependencies: 549 | has-tostringtag "^1.0.0" 550 | 551 | is-fullwidth-code-point@^3.0.0: 552 | version "3.0.0" 553 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 554 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 555 | 556 | is-lambda@^1.0.1: 557 | version "1.0.1" 558 | resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" 559 | integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== 560 | 561 | is-negative-zero@^2.0.2: 562 | version "2.0.2" 563 | resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" 564 | integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== 565 | 566 | is-number-object@^1.0.4: 567 | version "1.0.7" 568 | resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" 569 | integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== 570 | dependencies: 571 | has-tostringtag "^1.0.0" 572 | 573 | is-regex@^1.1.4: 574 | version "1.1.4" 575 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" 576 | integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== 577 | dependencies: 578 | call-bind "^1.0.2" 579 | has-tostringtag "^1.0.0" 580 | 581 | is-shared-array-buffer@^1.0.2: 582 | version "1.0.2" 583 | resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" 584 | integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== 585 | dependencies: 586 | call-bind "^1.0.2" 587 | 588 | is-string@^1.0.5, is-string@^1.0.7: 589 | version "1.0.7" 590 | resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" 591 | integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== 592 | dependencies: 593 | has-tostringtag "^1.0.0" 594 | 595 | is-symbol@^1.0.2, is-symbol@^1.0.3: 596 | version "1.0.4" 597 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" 598 | integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== 599 | dependencies: 600 | has-symbols "^1.0.2" 601 | 602 | is-weakref@^1.0.2: 603 | version "1.0.2" 604 | resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" 605 | integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== 606 | dependencies: 607 | call-bind "^1.0.2" 608 | 609 | isexe@^2.0.0: 610 | version "2.0.0" 611 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 612 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 613 | 614 | json-parse-better-errors@^1.0.1: 615 | version "1.0.2" 616 | resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" 617 | integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== 618 | 619 | load-json-file@^4.0.0: 620 | version "4.0.0" 621 | resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" 622 | integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== 623 | dependencies: 624 | graceful-fs "^4.1.2" 625 | parse-json "^4.0.0" 626 | pify "^3.0.0" 627 | strip-bom "^3.0.0" 628 | 629 | lru-cache@^6.0.0: 630 | version "6.0.0" 631 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 632 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 633 | dependencies: 634 | yallist "^4.0.0" 635 | 636 | lru-cache@^7.7.1: 637 | version "7.14.0" 638 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.0.tgz#21be64954a4680e303a09e9468f880b98a0b3c7f" 639 | integrity sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ== 640 | 641 | make-fetch-happen@^10.0.3: 642 | version "10.2.1" 643 | resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" 644 | integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== 645 | dependencies: 646 | agentkeepalive "^4.2.1" 647 | cacache "^16.1.0" 648 | http-cache-semantics "^4.1.0" 649 | http-proxy-agent "^5.0.0" 650 | https-proxy-agent "^5.0.0" 651 | is-lambda "^1.0.1" 652 | lru-cache "^7.7.1" 653 | minipass "^3.1.6" 654 | minipass-collect "^1.0.2" 655 | minipass-fetch "^2.0.3" 656 | minipass-flush "^1.0.5" 657 | minipass-pipeline "^1.2.4" 658 | negotiator "^0.6.3" 659 | promise-retry "^2.0.1" 660 | socks-proxy-agent "^7.0.0" 661 | ssri "^9.0.0" 662 | 663 | memorystream@^0.3.1: 664 | version "0.3.1" 665 | resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" 666 | integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== 667 | 668 | minimatch@^3.0.4, minimatch@^3.1.1: 669 | version "3.1.2" 670 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 671 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 672 | dependencies: 673 | brace-expansion "^1.1.7" 674 | 675 | minimatch@^5.0.1: 676 | version "5.1.0" 677 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" 678 | integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== 679 | dependencies: 680 | brace-expansion "^2.0.1" 681 | 682 | minipass-collect@^1.0.2: 683 | version "1.0.2" 684 | resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" 685 | integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== 686 | dependencies: 687 | minipass "^3.0.0" 688 | 689 | minipass-fetch@^2.0.3: 690 | version "2.1.2" 691 | resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" 692 | integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== 693 | dependencies: 694 | minipass "^3.1.6" 695 | minipass-sized "^1.0.3" 696 | minizlib "^2.1.2" 697 | optionalDependencies: 698 | encoding "^0.1.13" 699 | 700 | minipass-flush@^1.0.5: 701 | version "1.0.5" 702 | resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" 703 | integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== 704 | dependencies: 705 | minipass "^3.0.0" 706 | 707 | minipass-pipeline@^1.2.4: 708 | version "1.2.4" 709 | resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" 710 | integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== 711 | dependencies: 712 | minipass "^3.0.0" 713 | 714 | minipass-sized@^1.0.3: 715 | version "1.0.3" 716 | resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" 717 | integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== 718 | dependencies: 719 | minipass "^3.0.0" 720 | 721 | minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: 722 | version "3.3.4" 723 | resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" 724 | integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== 725 | dependencies: 726 | yallist "^4.0.0" 727 | 728 | minizlib@^2.1.1, minizlib@^2.1.2: 729 | version "2.1.2" 730 | resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" 731 | integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== 732 | dependencies: 733 | minipass "^3.0.0" 734 | yallist "^4.0.0" 735 | 736 | mkdirp@^1.0.3, mkdirp@^1.0.4: 737 | version "1.0.4" 738 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 739 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 740 | 741 | ms@2.1.2: 742 | version "2.1.2" 743 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 744 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 745 | 746 | ms@^2.0.0: 747 | version "2.1.3" 748 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 749 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 750 | 751 | nan@^2.17.0: 752 | version "2.17.0" 753 | resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" 754 | integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== 755 | 756 | negotiator@^0.6.3: 757 | version "0.6.3" 758 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" 759 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== 760 | 761 | nice-try@^1.0.4: 762 | version "1.0.5" 763 | resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" 764 | integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== 765 | 766 | node-gyp@^9.3.0: 767 | version "9.3.0" 768 | resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.0.tgz#f8eefe77f0ad8edb3b3b898409b53e697642b319" 769 | integrity sha512-A6rJWfXFz7TQNjpldJ915WFb1LnhO4lIve3ANPbWreuEoLoKlFT3sxIepPBkLhM27crW8YmN+pjlgbasH6cH/Q== 770 | dependencies: 771 | env-paths "^2.2.0" 772 | glob "^7.1.4" 773 | graceful-fs "^4.2.6" 774 | make-fetch-happen "^10.0.3" 775 | nopt "^6.0.0" 776 | npmlog "^6.0.0" 777 | rimraf "^3.0.2" 778 | semver "^7.3.5" 779 | tar "^6.1.2" 780 | which "^2.0.2" 781 | 782 | nopt@^6.0.0: 783 | version "6.0.0" 784 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" 785 | integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== 786 | dependencies: 787 | abbrev "^1.0.0" 788 | 789 | normalize-package-data@^2.3.2: 790 | version "2.5.0" 791 | resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" 792 | integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== 793 | dependencies: 794 | hosted-git-info "^2.1.4" 795 | resolve "^1.10.0" 796 | semver "2 || 3 || 4 || 5" 797 | validate-npm-package-license "^3.0.1" 798 | 799 | npm-run-all@^4.1.5: 800 | version "4.1.5" 801 | resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" 802 | integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== 803 | dependencies: 804 | ansi-styles "^3.2.1" 805 | chalk "^2.4.1" 806 | cross-spawn "^6.0.5" 807 | memorystream "^0.3.1" 808 | minimatch "^3.0.4" 809 | pidtree "^0.3.0" 810 | read-pkg "^3.0.0" 811 | shell-quote "^1.6.1" 812 | string.prototype.padend "^3.0.0" 813 | 814 | npmlog@^6.0.0: 815 | version "6.0.2" 816 | resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" 817 | integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== 818 | dependencies: 819 | are-we-there-yet "^3.0.0" 820 | console-control-strings "^1.1.0" 821 | gauge "^4.0.3" 822 | set-blocking "^2.0.0" 823 | 824 | object-inspect@^1.12.2, object-inspect@^1.9.0: 825 | version "1.12.2" 826 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" 827 | integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== 828 | 829 | object-keys@^1.1.1: 830 | version "1.1.1" 831 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 832 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 833 | 834 | object.assign@^4.1.4: 835 | version "4.1.4" 836 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" 837 | integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== 838 | dependencies: 839 | call-bind "^1.0.2" 840 | define-properties "^1.1.4" 841 | has-symbols "^1.0.3" 842 | object-keys "^1.1.1" 843 | 844 | once@^1.3.0: 845 | version "1.4.0" 846 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 847 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 848 | dependencies: 849 | wrappy "1" 850 | 851 | p-map@^4.0.0: 852 | version "4.0.0" 853 | resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" 854 | integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== 855 | dependencies: 856 | aggregate-error "^3.0.0" 857 | 858 | parse-json@^4.0.0: 859 | version "4.0.0" 860 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" 861 | integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== 862 | dependencies: 863 | error-ex "^1.3.1" 864 | json-parse-better-errors "^1.0.1" 865 | 866 | path-is-absolute@^1.0.0: 867 | version "1.0.1" 868 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 869 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 870 | 871 | path-key@^2.0.1: 872 | version "2.0.1" 873 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 874 | integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== 875 | 876 | path-parse@^1.0.7: 877 | version "1.0.7" 878 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 879 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 880 | 881 | path-type@^3.0.0: 882 | version "3.0.0" 883 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" 884 | integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== 885 | dependencies: 886 | pify "^3.0.0" 887 | 888 | pidtree@^0.3.0: 889 | version "0.3.1" 890 | resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" 891 | integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== 892 | 893 | pify@^3.0.0: 894 | version "3.0.0" 895 | resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" 896 | integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== 897 | 898 | promise-inflight@^1.0.1: 899 | version "1.0.1" 900 | resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" 901 | integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== 902 | 903 | promise-retry@^2.0.1: 904 | version "2.0.1" 905 | resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" 906 | integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== 907 | dependencies: 908 | err-code "^2.0.2" 909 | retry "^0.12.0" 910 | 911 | read-pkg@^3.0.0: 912 | version "3.0.0" 913 | resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" 914 | integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== 915 | dependencies: 916 | load-json-file "^4.0.0" 917 | normalize-package-data "^2.3.2" 918 | path-type "^3.0.0" 919 | 920 | readable-stream@^3.6.0: 921 | version "3.6.0" 922 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" 923 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== 924 | dependencies: 925 | inherits "^2.0.3" 926 | string_decoder "^1.1.1" 927 | util-deprecate "^1.0.1" 928 | 929 | regexp.prototype.flags@^1.4.3: 930 | version "1.4.3" 931 | resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" 932 | integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== 933 | dependencies: 934 | call-bind "^1.0.2" 935 | define-properties "^1.1.3" 936 | functions-have-names "^1.2.2" 937 | 938 | resolve@^1.10.0: 939 | version "1.22.1" 940 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 941 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 942 | dependencies: 943 | is-core-module "^2.9.0" 944 | path-parse "^1.0.7" 945 | supports-preserve-symlinks-flag "^1.0.0" 946 | 947 | retry@^0.12.0: 948 | version "0.12.0" 949 | resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" 950 | integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== 951 | 952 | rimraf@^3.0.2: 953 | version "3.0.2" 954 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" 955 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 956 | dependencies: 957 | glob "^7.1.3" 958 | 959 | safe-buffer@~5.2.0: 960 | version "5.2.1" 961 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 962 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 963 | 964 | safe-regex-test@^1.0.0: 965 | version "1.0.0" 966 | resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" 967 | integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== 968 | dependencies: 969 | call-bind "^1.0.2" 970 | get-intrinsic "^1.1.3" 971 | is-regex "^1.1.4" 972 | 973 | "safer-buffer@>= 2.1.2 < 3.0.0": 974 | version "2.1.2" 975 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 976 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 977 | 978 | "semver@2 || 3 || 4 || 5", semver@^5.5.0: 979 | version "5.7.1" 980 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 981 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 982 | 983 | semver@^7.3.5: 984 | version "7.3.8" 985 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" 986 | integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== 987 | dependencies: 988 | lru-cache "^6.0.0" 989 | 990 | set-blocking@^2.0.0: 991 | version "2.0.0" 992 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 993 | integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== 994 | 995 | shebang-command@^1.2.0: 996 | version "1.2.0" 997 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 998 | integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== 999 | dependencies: 1000 | shebang-regex "^1.0.0" 1001 | 1002 | shebang-regex@^1.0.0: 1003 | version "1.0.0" 1004 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 1005 | integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== 1006 | 1007 | shell-quote@^1.6.1: 1008 | version "1.7.4" 1009 | resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" 1010 | integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== 1011 | 1012 | side-channel@^1.0.4: 1013 | version "1.0.4" 1014 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" 1015 | integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== 1016 | dependencies: 1017 | call-bind "^1.0.0" 1018 | get-intrinsic "^1.0.2" 1019 | object-inspect "^1.9.0" 1020 | 1021 | signal-exit@^3.0.7: 1022 | version "3.0.7" 1023 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" 1024 | integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== 1025 | 1026 | smart-buffer@^4.2.0: 1027 | version "4.2.0" 1028 | resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" 1029 | integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== 1030 | 1031 | socks-proxy-agent@^7.0.0: 1032 | version "7.0.0" 1033 | resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" 1034 | integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== 1035 | dependencies: 1036 | agent-base "^6.0.2" 1037 | debug "^4.3.3" 1038 | socks "^2.6.2" 1039 | 1040 | socks@^2.6.2: 1041 | version "2.7.1" 1042 | resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" 1043 | integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== 1044 | dependencies: 1045 | ip "^2.0.0" 1046 | smart-buffer "^4.2.0" 1047 | 1048 | spdx-correct@^3.0.0: 1049 | version "3.1.1" 1050 | resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" 1051 | integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== 1052 | dependencies: 1053 | spdx-expression-parse "^3.0.0" 1054 | spdx-license-ids "^3.0.0" 1055 | 1056 | spdx-exceptions@^2.1.0: 1057 | version "2.3.0" 1058 | resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" 1059 | integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== 1060 | 1061 | spdx-expression-parse@^3.0.0: 1062 | version "3.0.1" 1063 | resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" 1064 | integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== 1065 | dependencies: 1066 | spdx-exceptions "^2.1.0" 1067 | spdx-license-ids "^3.0.0" 1068 | 1069 | spdx-license-ids@^3.0.0: 1070 | version "3.0.12" 1071 | resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" 1072 | integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== 1073 | 1074 | ssri@^9.0.0: 1075 | version "9.0.1" 1076 | resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" 1077 | integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== 1078 | dependencies: 1079 | minipass "^3.1.1" 1080 | 1081 | "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: 1082 | version "4.2.3" 1083 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" 1084 | integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== 1085 | dependencies: 1086 | emoji-regex "^8.0.0" 1087 | is-fullwidth-code-point "^3.0.0" 1088 | strip-ansi "^6.0.1" 1089 | 1090 | string.prototype.padend@^3.0.0: 1091 | version "3.1.3" 1092 | resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1" 1093 | integrity sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg== 1094 | dependencies: 1095 | call-bind "^1.0.2" 1096 | define-properties "^1.1.3" 1097 | es-abstract "^1.19.1" 1098 | 1099 | string.prototype.trimend@^1.0.5: 1100 | version "1.0.5" 1101 | resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" 1102 | integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== 1103 | dependencies: 1104 | call-bind "^1.0.2" 1105 | define-properties "^1.1.4" 1106 | es-abstract "^1.19.5" 1107 | 1108 | string.prototype.trimstart@^1.0.5: 1109 | version "1.0.5" 1110 | resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" 1111 | integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== 1112 | dependencies: 1113 | call-bind "^1.0.2" 1114 | define-properties "^1.1.4" 1115 | es-abstract "^1.19.5" 1116 | 1117 | string_decoder@^1.1.1: 1118 | version "1.3.0" 1119 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" 1120 | integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== 1121 | dependencies: 1122 | safe-buffer "~5.2.0" 1123 | 1124 | strip-ansi@^6.0.1: 1125 | version "6.0.1" 1126 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" 1127 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1128 | dependencies: 1129 | ansi-regex "^5.0.1" 1130 | 1131 | strip-bom@^3.0.0: 1132 | version "3.0.0" 1133 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 1134 | integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== 1135 | 1136 | supports-color@^5.3.0: 1137 | version "5.5.0" 1138 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 1139 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 1140 | dependencies: 1141 | has-flag "^3.0.0" 1142 | 1143 | supports-preserve-symlinks-flag@^1.0.0: 1144 | version "1.0.0" 1145 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 1146 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 1147 | 1148 | tar@^6.1.11, tar@^6.1.2: 1149 | version "6.1.11" 1150 | resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" 1151 | integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== 1152 | dependencies: 1153 | chownr "^2.0.0" 1154 | fs-minipass "^2.0.0" 1155 | minipass "^3.0.0" 1156 | minizlib "^2.1.1" 1157 | mkdirp "^1.0.3" 1158 | yallist "^4.0.0" 1159 | 1160 | typescript@^4.8.4: 1161 | version "4.8.4" 1162 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" 1163 | integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== 1164 | 1165 | unbox-primitive@^1.0.2: 1166 | version "1.0.2" 1167 | resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" 1168 | integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== 1169 | dependencies: 1170 | call-bind "^1.0.2" 1171 | has-bigints "^1.0.2" 1172 | has-symbols "^1.0.3" 1173 | which-boxed-primitive "^1.0.2" 1174 | 1175 | unique-filename@^2.0.0: 1176 | version "2.0.1" 1177 | resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" 1178 | integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== 1179 | dependencies: 1180 | unique-slug "^3.0.0" 1181 | 1182 | unique-slug@^3.0.0: 1183 | version "3.0.0" 1184 | resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" 1185 | integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== 1186 | dependencies: 1187 | imurmurhash "^0.1.4" 1188 | 1189 | util-deprecate@^1.0.1: 1190 | version "1.0.2" 1191 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1192 | integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== 1193 | 1194 | validate-npm-package-license@^3.0.1: 1195 | version "3.0.4" 1196 | resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" 1197 | integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== 1198 | dependencies: 1199 | spdx-correct "^3.0.0" 1200 | spdx-expression-parse "^3.0.0" 1201 | 1202 | which-boxed-primitive@^1.0.2: 1203 | version "1.0.2" 1204 | resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" 1205 | integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== 1206 | dependencies: 1207 | is-bigint "^1.0.1" 1208 | is-boolean-object "^1.1.0" 1209 | is-number-object "^1.0.4" 1210 | is-string "^1.0.5" 1211 | is-symbol "^1.0.3" 1212 | 1213 | which@^1.2.9: 1214 | version "1.3.1" 1215 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1216 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 1217 | dependencies: 1218 | isexe "^2.0.0" 1219 | 1220 | which@^2.0.2: 1221 | version "2.0.2" 1222 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 1223 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1224 | dependencies: 1225 | isexe "^2.0.0" 1226 | 1227 | wide-align@^1.1.5: 1228 | version "1.1.5" 1229 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" 1230 | integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== 1231 | dependencies: 1232 | string-width "^1.0.2 || 2 || 3 || 4" 1233 | 1234 | wrappy@1: 1235 | version "1.0.2" 1236 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1237 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1238 | 1239 | yallist@^4.0.0: 1240 | version "4.0.0" 1241 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1242 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1243 | --------------------------------------------------------------------------------