diff --git a/.dockerignore b/.dockerignore index 3e6ef3fe4..0bc56885c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ /.idea /node_modules -/data +/data* /cypress /out /test diff --git a/.eslintrc.js b/.eslintrc.js index 4713799d7..0f19793aa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,7 @@ module.exports = { ignorePatterns: [ - "test/*", + "test/*.js", + "test/cypress", "server/modules/apicache/*", "src/util.js" ], @@ -14,6 +15,7 @@ module.exports = { extends: [ "eslint:recommended", "plugin:vue/vue3-recommended", + "plugin:jsdoc/recommended-error", ], parser: "vue-eslint-parser", parserOptions: { @@ -21,6 +23,9 @@ module.exports = { sourceType: "module", requireConfigFile: false, }, + plugins: [ + "jsdoc" + ], rules: { "yoda": "error", eqeqeq: [ "warn", "smart" ], @@ -97,7 +102,43 @@ module.exports = { }], "no-control-regex": "off", "one-var": [ "error", "never" ], - "max-statements-per-line": [ "error", { "max": 1 }] + "max-statements-per-line": [ "error", { "max": 1 }], + "jsdoc/check-tag-names": [ + "error", + { + "definedTags": [ "link" ] + } + ], + "jsdoc/no-undefined-types": "off", + "jsdoc/no-defaults": [ + "error", + { "noOptionalParamNames": true } + ], + "jsdoc/require-throws": "warn", + "jsdoc/require-jsdoc": [ + "error", + { + "require": { + "FunctionDeclaration": true, + "MethodDefinition": true, + } + } + ], + "jsdoc/no-blank-block-descriptions": "error", + "jsdoc/require-returns-description": "warn", + "jsdoc/require-returns-check": [ + "error", + { "reportMissingReturnForUndefinedTypes": false } + ], + "jsdoc/require-returns": [ + "warn", + { + "forceRequireReturn": true, + "forceReturnsWithAsync": true + } + ], + "jsdoc/require-param-type": "warn", + "jsdoc/require-param-description": "warn" }, "overrides": [ { diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index 43752e38a..14f776dff 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -71,27 +71,28 @@ jobs: - run: git config --global core.autocrlf false # Mainly for Windows - uses: actions/checkout@v3 - - name: Use Node.js 14 + - name: Use Node.js 20 uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 20 - run: npm install - run: npm run lint - e2e-tests: - needs: [ check-linters ] - runs-on: ubuntu-latest - steps: - - run: git config --global core.autocrlf false # Mainly for Windows - - uses: actions/checkout@v3 - - - name: Use Node.js 14 - uses: actions/setup-node@v3 - with: - node-version: 14 - - run: npm install - - run: npm run build - - run: npm run cy:test +# TODO: Temporarily disable, as it cannot pass the test in 2.0.0 yet +# e2e-tests: +# needs: [ check-linters ] +# runs-on: ubuntu-latest +# steps: +# - run: git config --global core.autocrlf false # Mainly for Windows +# - uses: actions/checkout@v3 +# +# - name: Use Node.js 14 +# uses: actions/setup-node@v3 +# with: +# node-version: 14 +# - run: npm install +# - run: npm run build +# - run: npm run cy:test frontend-unit-tests: needs: [ check-linters ] diff --git a/.gitignore b/.gitignore index 009b15f10..169b58204 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist-ssr /data !/data/.gitkeep +/data* .vscode /private diff --git a/db/knex_init_db.js b/db/knex_init_db.js new file mode 100644 index 000000000..2a27fa1d7 --- /dev/null +++ b/db/knex_init_db.js @@ -0,0 +1,559 @@ +const { R } = require("redbean-node"); +const { log } = require("../src/util"); + +/** + * ⚠️⚠️⚠️⚠️⚠️⚠️ DO NOT ADD ANYTHING HERE! + * IF YOU NEED TO ADD FIELDS, ADD IT TO ./db/knex_migrations + * See ./db/knex_migrations/README.md for more information + * @returns {Promise} + */ +async function createTables() { + log.info("mariadb", "Creating basic tables for MariaDB"); + const knex = R.knex; + + // TODO: Should check later if it is really the final patch sql file. + + // docker_host + await knex.schema.createTable("docker_host", (table) => { + table.increments("id"); + table.integer("user_id").unsigned().notNullable(); + table.string("docker_daemon", 255); + table.string("docker_type", 255); + table.string("name", 255); + }); + + // group + await knex.schema.createTable("group", (table) => { + table.increments("id"); + table.string("name", 255).notNullable(); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.boolean("public").notNullable().defaultTo(false); + table.boolean("active").notNullable().defaultTo(true); + table.integer("weight").notNullable().defaultTo(1000); + table.integer("status_page_id").unsigned(); + }); + + // proxy + await knex.schema.createTable("proxy", (table) => { + table.increments("id"); + table.integer("user_id").unsigned().notNullable(); + table.string("protocol", 10).notNullable(); + table.string("host", 255).notNullable(); + table.smallint("port").notNullable(); // TODO: Maybe a issue with MariaDB, need migration to int + table.boolean("auth").notNullable(); + table.string("username", 255).nullable(); + table.string("password", 255).nullable(); + table.boolean("active").notNullable().defaultTo(true); + table.boolean("default").notNullable().defaultTo(false); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + + table.index("user_id", "proxy_user_id"); + }); + + // user + await knex.schema.createTable("user", (table) => { + table.increments("id"); + table.string("username", 255).notNullable().unique().collate("utf8_general_ci"); + table.string("password", 255); + table.boolean("active").notNullable().defaultTo(true); + table.string("timezone", 150); + table.string("twofa_secret", 64); + table.boolean("twofa_status").notNullable().defaultTo(false); + table.string("twofa_last_token", 6); + }); + + // monitor + await knex.schema.createTable("monitor", (table) => { + table.increments("id"); + table.string("name", 150); + table.boolean("active").notNullable().defaultTo(true); + table.integer("user_id").unsigned() + .references("id").inTable("user") + .onDelete("SET NULL") + .onUpdate("CASCADE"); + table.integer("interval").notNullable().defaultTo(20); + table.text("url"); + table.string("type", 20); + table.integer("weight").defaultTo(2000); + table.string("hostname", 255); + table.integer("port"); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.string("keyword", 255); + table.integer("maxretries").notNullable().defaultTo(0); + table.boolean("ignore_tls").notNullable().defaultTo(false); + table.boolean("upside_down").notNullable().defaultTo(false); + table.integer("maxredirects").notNullable().defaultTo(10); + table.text("accepted_statuscodes_json").notNullable().defaultTo("[\"200-299\"]"); + table.string("dns_resolve_type", 5); + table.string("dns_resolve_server", 255); + table.string("dns_last_result", 255); + table.integer("retry_interval").notNullable().defaultTo(0); + table.string("push_token", 20).defaultTo(null); + table.text("method").notNullable().defaultTo("GET"); + table.text("body").defaultTo(null); + table.text("headers").defaultTo(null); + table.text("basic_auth_user").defaultTo(null); + table.text("basic_auth_pass").defaultTo(null); + table.integer("docker_host").unsigned() + .references("id").inTable("docker_host"); + table.string("docker_container", 255); + table.integer("proxy_id").unsigned() + .references("id").inTable("proxy"); + table.boolean("expiry_notification").defaultTo(true); + table.text("mqtt_topic"); + table.string("mqtt_success_message", 255); + table.string("mqtt_username", 255); + table.string("mqtt_password", 255); + table.string("database_connection_string", 2000); + table.text("database_query"); + table.string("auth_method", 250); + table.text("auth_domain"); + table.text("auth_workstation"); + table.string("grpc_url", 255).defaultTo(null); + table.text("grpc_protobuf").defaultTo(null); + table.text("grpc_body").defaultTo(null); + table.text("grpc_metadata").defaultTo(null); + table.text("grpc_method").defaultTo(null); + table.text("grpc_service_name").defaultTo(null); + table.boolean("grpc_enable_tls").notNullable().defaultTo(false); + table.string("radius_username", 255); + table.string("radius_password", 255); + table.string("radius_calling_station_id", 50); + table.string("radius_called_station_id", 50); + table.string("radius_secret", 255); + table.integer("resend_interval").notNullable().defaultTo(0); + table.integer("packet_size").notNullable().defaultTo(56); + table.string("game", 255); + }); + + // heartbeat + await knex.schema.createTable("heartbeat", (table) => { + table.increments("id"); + table.boolean("important").notNullable().defaultTo(false); + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.smallint("status").notNullable(); + + table.text("msg"); + table.datetime("time").notNullable(); + table.integer("ping"); + table.integer("duration").notNullable().defaultTo(0); + table.integer("down_count").notNullable().defaultTo(0); + + table.index("important"); + table.index([ "monitor_id", "time" ], "monitor_time_index"); + table.index("monitor_id"); + table.index([ "monitor_id", "important", "time" ], "monitor_important_time_index"); + }); + + // incident + await knex.schema.createTable("incident", (table) => { + table.increments("id"); + table.string("title", 255).notNullable(); + table.text("content", 255).notNullable(); + table.string("style", 30).notNullable().defaultTo("warning"); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.datetime("last_updated_date"); + table.boolean("pin").notNullable().defaultTo(true); + table.boolean("active").notNullable().defaultTo(true); + table.integer("status_page_id").unsigned(); + }); + + // maintenance + await knex.schema.createTable("maintenance", (table) => { + table.increments("id"); + table.string("title", 150).notNullable(); + table.text("description").notNullable(); + table.integer("user_id").unsigned() + .references("id").inTable("user") + .onDelete("SET NULL") + .onUpdate("CASCADE"); + table.boolean("active").notNullable().defaultTo(true); + table.string("strategy", 50).notNullable().defaultTo("single"); + table.datetime("start_date"); + table.datetime("end_date"); + table.time("start_time"); + table.time("end_time"); + table.string("weekdays", 250).defaultTo("[]"); + table.text("days_of_month").defaultTo("[]"); + table.integer("interval_day"); + + table.index("active"); + table.index([ "strategy", "active" ], "manual_active"); + table.index("user_id", "maintenance_user_id"); + }); + + // status_page + await knex.schema.createTable("status_page", (table) => { + table.increments("id"); + table.string("slug", 255).notNullable().unique().collate("utf8_general_ci"); + table.string("title", 255).notNullable(); + table.text("description"); + table.string("icon", 255).notNullable(); + table.string("theme", 30).notNullable(); + table.boolean("published").notNullable().defaultTo(true); + table.boolean("search_engine_index").notNullable().defaultTo(true); + table.boolean("show_tags").notNullable().defaultTo(false); + table.string("password"); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.datetime("modified_date").notNullable().defaultTo(knex.fn.now()); + table.text("footer_text"); + table.text("custom_css"); + table.boolean("show_powered_by").notNullable().defaultTo(true); + table.string("google_analytics_tag_id"); + }); + + // maintenance_status_page + await knex.schema.createTable("maintenance_status_page", (table) => { + table.increments("id"); + + table.integer("status_page_id").unsigned().notNullable() + .references("id").inTable("status_page") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + + table.integer("maintenance_id").unsigned().notNullable() + .references("id").inTable("maintenance") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + }); + + // maintenance_timeslot + await knex.schema.createTable("maintenance_timeslot", (table) => { + table.increments("id"); + table.integer("maintenance_id").unsigned().notNullable() + .references("id").inTable("maintenance") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.datetime("start_date").notNullable(); + table.datetime("end_date"); + table.boolean("generated_next").defaultTo(false); + + table.index("maintenance_id"); + table.index([ "maintenance_id", "start_date", "end_date" ], "active_timeslot_index"); + table.index("generated_next", "generated_next_index"); + }); + + // monitor_group + await knex.schema.createTable("monitor_group", (table) => { + table.increments("id"); + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("group_id").unsigned().notNullable() + .references("id").inTable("group") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("weight").notNullable().defaultTo(1000); + table.boolean("send_url").notNullable().defaultTo(false); + + table.index([ "monitor_id", "group_id" ], "fk"); + }); + // monitor_maintenance + await knex.schema.createTable("monitor_maintenance", (table) => { + table.increments("id"); + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("maintenance_id").unsigned().notNullable() + .references("id").inTable("maintenance") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + + table.index("maintenance_id", "maintenance_id_index2"); + table.index("monitor_id", "monitor_id_index"); + }); + + // notification + await knex.schema.createTable("notification", (table) => { + table.increments("id"); + table.string("name", 255); + table.string("config", 255); // TODO: should use TEXT! + table.boolean("active").notNullable().defaultTo(true); + table.integer("user_id").unsigned(); + table.boolean("is_default").notNullable().defaultTo(false); + }); + + // monitor_notification + await knex.schema.createTable("monitor_notification", (table) => { + table.increments("id").unsigned(); // TODO: no auto increment???? + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("notification_id").unsigned().notNullable() + .references("id").inTable("notification") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + + table.index([ "monitor_id", "notification_id" ], "monitor_notification_index"); + }); + + // tag + await knex.schema.createTable("tag", (table) => { + table.increments("id"); + table.string("name", 255).notNullable(); + table.string("color", 255).notNullable(); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + }); + + // monitor_tag + await knex.schema.createTable("monitor_tag", (table) => { + table.increments("id"); + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("tag_id").unsigned().notNullable() + .references("id").inTable("tag") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.text("value"); + }); + + // monitor_tls_info + await knex.schema.createTable("monitor_tls_info", (table) => { + table.increments("id"); + table.integer("monitor_id").unsigned().notNullable(); //TODO: no fk ? + table.text("info_json"); + }); + + // notification_sent_history + await knex.schema.createTable("notification_sent_history", (table) => { + table.increments("id"); + table.string("type", 50).notNullable(); + table.integer("monitor_id").unsigned().notNullable(); + table.integer("days").notNullable(); + table.unique([ "type", "monitor_id", "days" ]); + table.index([ "type", "monitor_id", "days" ], "good_index"); + }); + + // setting + await knex.schema.createTable("setting", (table) => { + table.increments("id"); + table.string("key", 200).notNullable().unique().collate("utf8_general_ci"); + table.text("value"); + table.string("type", 20); + }); + + // status_page_cname + await knex.schema.createTable("status_page_cname", (table) => { + table.increments("id"); + table.integer("status_page_id").unsigned() + .references("id").inTable("status_page") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.string("domain").notNullable().unique().collate("utf8_general_ci"); + }); + + /********************* + * Converted Patch here + *********************/ + + // 2023-06-30-1348-http-body-encoding.js + // ALTER TABLE monitor ADD http_body_encoding VARCHAR(25); + // UPDATE monitor SET http_body_encoding = 'json' WHERE (type = 'http' or type = 'keyword') AND http_body_encoding IS NULL; + await knex.schema.table("monitor", function (table) { + table.string("http_body_encoding", 25); + }); + + await knex("monitor") + .where(function () { + this.where("type", "http").orWhere("type", "keyword"); + }) + .whereNull("http_body_encoding") + .update({ + http_body_encoding: "json", + }); + + // 2023-06-30-1354-add-description-monitor.js + // ALTER TABLE monitor ADD description TEXT default null; + await knex.schema.table("monitor", function (table) { + table.text("description").defaultTo(null); + }); + + // 2023-06-30-1357-api-key-table.js + /* + CREATE TABLE [api_key] ( + [id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + [key] VARCHAR(255) NOT NULL, + [name] VARCHAR(255) NOT NULL, + [user_id] INTEGER NOT NULL, + [created_date] DATETIME DEFAULT (DATETIME('now')) NOT NULL, + [active] BOOLEAN DEFAULT 1 NOT NULL, + [expires] DATETIME DEFAULT NULL, + CONSTRAINT FK_user FOREIGN KEY ([user_id]) REFERENCES [user]([id]) ON DELETE CASCADE ON UPDATE CASCADE + ); + */ + await knex.schema.createTable("api_key", function (table) { + table.increments("id").primary(); + table.string("key", 255).notNullable(); + table.string("name", 255).notNullable(); + table.integer("user_id").unsigned().notNullable() + .references("id").inTable("user") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.dateTime("created_date").defaultTo(knex.fn.now()).notNullable(); + table.boolean("active").defaultTo(1).notNullable(); + table.dateTime("expires").defaultTo(null); + }); + + // 2023-06-30-1400-monitor-tls.js + /* + ALTER TABLE monitor + ADD tls_ca TEXT default null; + + ALTER TABLE monitor + ADD tls_cert TEXT default null; + + ALTER TABLE monitor + ADD tls_key TEXT default null; + */ + await knex.schema.table("monitor", function (table) { + table.text("tls_ca").defaultTo(null); + table.text("tls_cert").defaultTo(null); + table.text("tls_key").defaultTo(null); + }); + + // 2023-06-30-1401-maintenance-cron.js + /* + -- 999 characters. https://stackoverflow.com/questions/46134830/maximum-length-for-cron-job + DROP TABLE maintenance_timeslot; + ALTER TABLE maintenance ADD cron TEXT; + ALTER TABLE maintenance ADD timezone VARCHAR(255); + ALTER TABLE maintenance ADD duration INTEGER; + */ + await knex.schema + .dropTableIfExists("maintenance_timeslot") + .table("maintenance", function (table) { + table.text("cron"); + table.string("timezone", 255); + table.integer("duration"); + }); + + // 2023-06-30-1413-add-parent-monitor.js. + /* + ALTER TABLE monitor + ADD parent INTEGER REFERENCES [monitor] ([id]) ON DELETE SET NULL ON UPDATE CASCADE; + */ + await knex.schema.table("monitor", function (table) { + table.integer("parent").unsigned() + .references("id").inTable("monitor") + .onDelete("SET NULL") + .onUpdate("CASCADE"); + }); + + /* + patch-add-invert-keyword.sql + ALTER TABLE monitor + ADD invert_keyword BOOLEAN default 0 not null; + */ + await knex.schema.table("monitor", function (table) { + table.boolean("invert_keyword").defaultTo(0).notNullable(); + }); + + /* + patch-added-json-query.sql + ALTER TABLE monitor + ADD json_path TEXT; + + ALTER TABLE monitor + ADD expected_value VARCHAR(255); + */ + await knex.schema.table("monitor", function (table) { + table.text("json_path"); + table.string("expected_value", 255); + }); + + /* + patch-added-kafka-producer.sql + + ALTER TABLE monitor + ADD kafka_producer_topic VARCHAR(255); + +ALTER TABLE monitor + ADD kafka_producer_brokers TEXT; + +ALTER TABLE monitor + ADD kafka_producer_ssl INTEGER; + +ALTER TABLE monitor + ADD kafka_producer_allow_auto_topic_creation VARCHAR(255); + +ALTER TABLE monitor + ADD kafka_producer_sasl_options TEXT; + +ALTER TABLE monitor + ADD kafka_producer_message TEXT; + */ + await knex.schema.table("monitor", function (table) { + table.string("kafka_producer_topic", 255); + table.text("kafka_producer_brokers"); + table.integer("kafka_producer_ssl"); + table.string("kafka_producer_allow_auto_topic_creation", 255); + table.text("kafka_producer_sasl_options"); + table.text("kafka_producer_message"); + }); + + /* + patch-add-certificate-expiry-status-page.sql + ALTER TABLE status_page + ADD show_certificate_expiry BOOLEAN default 0 NOT NULL; + */ + await knex.schema.table("status_page", function (table) { + table.boolean("show_certificate_expiry").defaultTo(0).notNullable(); + }); + + /* + patch-monitor-oauth-cc.sql + ALTER TABLE monitor + ADD oauth_client_id TEXT default null; + +ALTER TABLE monitor + ADD oauth_client_secret TEXT default null; + +ALTER TABLE monitor + ADD oauth_token_url TEXT default null; + +ALTER TABLE monitor + ADD oauth_scopes TEXT default null; + +ALTER TABLE monitor + ADD oauth_auth_method TEXT default null; + */ + await knex.schema.table("monitor", function (table) { + table.text("oauth_client_id").defaultTo(null); + table.text("oauth_client_secret").defaultTo(null); + table.text("oauth_token_url").defaultTo(null); + table.text("oauth_scopes").defaultTo(null); + table.text("oauth_auth_method").defaultTo(null); + }); + + /* + patch-add-timeout-monitor.sql + ALTER TABLE monitor + ADD timeout DOUBLE default 0 not null; + */ + await knex.schema.table("monitor", function (table) { + table.double("timeout").defaultTo(0).notNullable(); + }); + + /* + patch-add-gamedig-given-port.sql + ALTER TABLE monitor + ADD gamedig_given_port_only BOOLEAN default 1 not null; + */ + await knex.schema.table("monitor", function (table) { + table.boolean("gamedig_given_port_only").defaultTo(1).notNullable(); + }); + + log.info("mariadb", "Created basic tables for MariaDB"); +} + +module.exports = { + createTables, +}; diff --git a/db/knex_migrations/2023-08-16-0000-create-uptime.js b/db/knex_migrations/2023-08-16-0000-create-uptime.js new file mode 100644 index 000000000..ab899311c --- /dev/null +++ b/db/knex_migrations/2023-08-16-0000-create-uptime.js @@ -0,0 +1,41 @@ +exports.up = function (knex) { + return knex.schema + .createTable("stat_minutely", function (table) { + table.increments("id"); + table.comment("This table contains the minutely aggregate statistics for each monitor"); + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("timestamp") + .notNullable() + .comment("Unix timestamp rounded down to the nearest minute"); + table.float("ping").notNullable().comment("Average ping in milliseconds"); + table.smallint("up").notNullable(); + table.smallint("down").notNullable(); + + table.unique([ "monitor_id", "timestamp" ]); + }) + .createTable("stat_daily", function (table) { + table.increments("id"); + table.comment("This table contains the daily aggregate statistics for each monitor"); + table.integer("monitor_id").unsigned().notNullable() + .references("id").inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.integer("timestamp") + .notNullable() + .comment("Unix timestamp rounded down to the nearest day"); + table.float("ping").notNullable().comment("Average ping in milliseconds"); + table.smallint("up").notNullable(); + table.smallint("down").notNullable(); + + table.unique([ "monitor_id", "timestamp" ]); + }); +}; + +exports.down = function (knex) { + return knex.schema + .dropTable("stat_minutely") + .dropTable("stat_daily"); +}; diff --git a/db/knex_migrations/2023-08-18-0301-heartbeat.js b/db/knex_migrations/2023-08-18-0301-heartbeat.js new file mode 100644 index 000000000..fe4152b48 --- /dev/null +++ b/db/knex_migrations/2023-08-18-0301-heartbeat.js @@ -0,0 +1,16 @@ +exports.up = function (knex) { + // Add new column heartbeat.end_time + return knex.schema + .alterTable("heartbeat", function (table) { + table.datetime("end_time").nullable().defaultTo(null); + }); + +}; + +exports.down = function (knex) { + // Rename heartbeat.start_time to heartbeat.time + return knex.schema + .alterTable("heartbeat", function (table) { + table.dropColumn("end_time"); + }); +}; diff --git a/db/knex_migrations/README.md b/db/knex_migrations/README.md new file mode 100644 index 000000000..4bebe3482 --- /dev/null +++ b/db/knex_migrations/README.md @@ -0,0 +1,55 @@ +## Info + +https://knexjs.org/guide/migrations.html#knexfile-in-other-languages + +## Basic rules +- All tables must have a primary key named `id` +- Filename format: `YYYY-MM-DD-HHMM-patch-name.js` +- Avoid native SQL syntax, use knex methods, because Uptime Kuma supports SQLite and MariaDB. + +## Template + +```js +exports.up = function(knex) { + +}; + +exports.down = function(knex) { + +}; + +// exports.config = { transaction: false }; +``` + +## Example + +Filename: 2023-06-30-1348-create-user-and-product.js + +```js +exports.up = function(knex) { + return knex.schema + .createTable('user', function (table) { + table.increments('id'); + table.string('first_name', 255).notNullable(); + table.string('last_name', 255).notNullable(); + }) + .createTable('product', function (table) { + table.increments('id'); + table.decimal('price').notNullable(); + table.string('name', 1000).notNullable(); + }).then(() => { + knex("products").insert([ + { price: 10, name: "Apple" }, + { price: 20, name: "Orange" }, + ]); + }); +}; + +exports.down = function(knex) { + return knex.schema + .dropTable("product") + .dropTable("user"); +}; +``` + +https://knexjs.org/guide/migrations.html#transactions-in-migrations diff --git a/db/old_migrations/README.md b/db/old_migrations/README.md new file mode 100644 index 000000000..3b2bd9640 --- /dev/null +++ b/db/old_migrations/README.md @@ -0,0 +1,3 @@ +# Don't create a new migration file here + +Please go to ./db/knex_migrations/README.md diff --git a/db/patch-2fa-invalidate-used-token.sql b/db/old_migrations/patch-2fa-invalidate-used-token.sql similarity index 100% rename from db/patch-2fa-invalidate-used-token.sql rename to db/old_migrations/patch-2fa-invalidate-used-token.sql diff --git a/db/patch-2fa.sql b/db/old_migrations/patch-2fa.sql similarity index 100% rename from db/patch-2fa.sql rename to db/old_migrations/patch-2fa.sql diff --git a/db/patch-add-certificate-expiry-status-page.sql b/db/old_migrations/patch-add-certificate-expiry-status-page.sql similarity index 100% rename from db/patch-add-certificate-expiry-status-page.sql rename to db/old_migrations/patch-add-certificate-expiry-status-page.sql diff --git a/db/patch-add-clickable-status-page-link.sql b/db/old_migrations/patch-add-clickable-status-page-link.sql similarity index 100% rename from db/patch-add-clickable-status-page-link.sql rename to db/old_migrations/patch-add-clickable-status-page-link.sql diff --git a/db/patch-add-description-monitor.sql b/db/old_migrations/patch-add-description-monitor.sql similarity index 100% rename from db/patch-add-description-monitor.sql rename to db/old_migrations/patch-add-description-monitor.sql diff --git a/db/patch-add-docker-columns.sql b/db/old_migrations/patch-add-docker-columns.sql similarity index 100% rename from db/patch-add-docker-columns.sql rename to db/old_migrations/patch-add-docker-columns.sql diff --git a/db/patch-add-gamedig-given-port.sql b/db/old_migrations/patch-add-gamedig-given-port.sql similarity index 100% rename from db/patch-add-gamedig-given-port.sql rename to db/old_migrations/patch-add-gamedig-given-port.sql diff --git a/db/patch-add-gamedig-monitor.sql b/db/old_migrations/patch-add-gamedig-monitor.sql similarity index 100% rename from db/patch-add-gamedig-monitor.sql rename to db/old_migrations/patch-add-gamedig-monitor.sql diff --git a/db/patch-add-google-analytics-status-page-tag.sql b/db/old_migrations/patch-add-google-analytics-status-page-tag.sql similarity index 100% rename from db/patch-add-google-analytics-status-page-tag.sql rename to db/old_migrations/patch-add-google-analytics-status-page-tag.sql diff --git a/db/patch-add-invert-keyword.sql b/db/old_migrations/patch-add-invert-keyword.sql similarity index 100% rename from db/patch-add-invert-keyword.sql rename to db/old_migrations/patch-add-invert-keyword.sql diff --git a/db/patch-add-other-auth.sql b/db/old_migrations/patch-add-other-auth.sql similarity index 100% rename from db/patch-add-other-auth.sql rename to db/old_migrations/patch-add-other-auth.sql diff --git a/db/patch-add-parent-monitor.sql b/db/old_migrations/patch-add-parent-monitor.sql similarity index 100% rename from db/patch-add-parent-monitor.sql rename to db/old_migrations/patch-add-parent-monitor.sql diff --git a/db/patch-add-radius-monitor.sql b/db/old_migrations/patch-add-radius-monitor.sql similarity index 100% rename from db/patch-add-radius-monitor.sql rename to db/old_migrations/patch-add-radius-monitor.sql diff --git a/db/patch-add-retry-interval-monitor.sql b/db/old_migrations/patch-add-retry-interval-monitor.sql similarity index 100% rename from db/patch-add-retry-interval-monitor.sql rename to db/old_migrations/patch-add-retry-interval-monitor.sql diff --git a/db/patch-add-sqlserver-monitor.sql b/db/old_migrations/patch-add-sqlserver-monitor.sql similarity index 100% rename from db/patch-add-sqlserver-monitor.sql rename to db/old_migrations/patch-add-sqlserver-monitor.sql diff --git a/db/patch-add-timeout-monitor.sql b/db/old_migrations/patch-add-timeout-monitor.sql similarity index 100% rename from db/patch-add-timeout-monitor.sql rename to db/old_migrations/patch-add-timeout-monitor.sql diff --git a/db/patch-added-json-query.sql b/db/old_migrations/patch-added-json-query.sql similarity index 100% rename from db/patch-added-json-query.sql rename to db/old_migrations/patch-added-json-query.sql diff --git a/db/patch-added-kafka-producer.sql b/db/old_migrations/patch-added-kafka-producer.sql similarity index 100% rename from db/patch-added-kafka-producer.sql rename to db/old_migrations/patch-added-kafka-producer.sql diff --git a/db/patch-added-mqtt-monitor.sql b/db/old_migrations/patch-added-mqtt-monitor.sql similarity index 100% rename from db/patch-added-mqtt-monitor.sql rename to db/old_migrations/patch-added-mqtt-monitor.sql diff --git a/db/patch-api-key-table.sql b/db/old_migrations/patch-api-key-table.sql similarity index 100% rename from db/patch-api-key-table.sql rename to db/old_migrations/patch-api-key-table.sql diff --git a/db/patch-group-table.sql b/db/old_migrations/patch-group-table.sql similarity index 100% rename from db/patch-group-table.sql rename to db/old_migrations/patch-group-table.sql diff --git a/db/patch-grpc-monitor.sql b/db/old_migrations/patch-grpc-monitor.sql similarity index 100% rename from db/patch-grpc-monitor.sql rename to db/old_migrations/patch-grpc-monitor.sql diff --git a/db/patch-http-body-encoding.sql b/db/old_migrations/patch-http-body-encoding.sql similarity index 100% rename from db/patch-http-body-encoding.sql rename to db/old_migrations/patch-http-body-encoding.sql diff --git a/db/patch-http-monitor-method-body-and-headers.sql b/db/old_migrations/patch-http-monitor-method-body-and-headers.sql similarity index 100% rename from db/patch-http-monitor-method-body-and-headers.sql rename to db/old_migrations/patch-http-monitor-method-body-and-headers.sql diff --git a/db/patch-improve-performance.sql b/db/old_migrations/patch-improve-performance.sql similarity index 100% rename from db/patch-improve-performance.sql rename to db/old_migrations/patch-improve-performance.sql diff --git a/db/patch-incident-table.sql b/db/old_migrations/patch-incident-table.sql similarity index 100% rename from db/patch-incident-table.sql rename to db/old_migrations/patch-incident-table.sql diff --git a/db/patch-maintenance-cron.sql b/db/old_migrations/patch-maintenance-cron.sql similarity index 100% rename from db/patch-maintenance-cron.sql rename to db/old_migrations/patch-maintenance-cron.sql diff --git a/db/patch-maintenance-table2.sql b/db/old_migrations/patch-maintenance-table2.sql similarity index 100% rename from db/patch-maintenance-table2.sql rename to db/old_migrations/patch-maintenance-table2.sql diff --git a/db/patch-monitor-add-resend-interval.sql b/db/old_migrations/patch-monitor-add-resend-interval.sql similarity index 100% rename from db/patch-monitor-add-resend-interval.sql rename to db/old_migrations/patch-monitor-add-resend-interval.sql diff --git a/db/patch-monitor-basic-auth.sql b/db/old_migrations/patch-monitor-basic-auth.sql similarity index 100% rename from db/patch-monitor-basic-auth.sql rename to db/old_migrations/patch-monitor-basic-auth.sql diff --git a/db/patch-monitor-expiry-notification.sql b/db/old_migrations/patch-monitor-expiry-notification.sql similarity index 100% rename from db/patch-monitor-expiry-notification.sql rename to db/old_migrations/patch-monitor-expiry-notification.sql diff --git a/db/patch-monitor-oauth-cc.sql b/db/old_migrations/patch-monitor-oauth-cc.sql similarity index 100% rename from db/patch-monitor-oauth-cc.sql rename to db/old_migrations/patch-monitor-oauth-cc.sql diff --git a/db/patch-monitor-push_token.sql b/db/old_migrations/patch-monitor-push_token.sql similarity index 100% rename from db/patch-monitor-push_token.sql rename to db/old_migrations/patch-monitor-push_token.sql diff --git a/db/patch-monitor-tls.sql b/db/old_migrations/patch-monitor-tls.sql similarity index 100% rename from db/patch-monitor-tls.sql rename to db/old_migrations/patch-monitor-tls.sql diff --git a/db/patch-notification_sent_history.sql b/db/old_migrations/patch-notification_sent_history.sql similarity index 100% rename from db/patch-notification_sent_history.sql rename to db/old_migrations/patch-notification_sent_history.sql diff --git a/db/patch-ping-packet-size.sql b/db/old_migrations/patch-ping-packet-size.sql similarity index 100% rename from db/patch-ping-packet-size.sql rename to db/old_migrations/patch-ping-packet-size.sql diff --git a/db/patch-proxy.sql b/db/old_migrations/patch-proxy.sql similarity index 100% rename from db/patch-proxy.sql rename to db/old_migrations/patch-proxy.sql diff --git a/db/patch-setting-value-type.sql b/db/old_migrations/patch-setting-value-type.sql similarity index 100% rename from db/patch-setting-value-type.sql rename to db/old_migrations/patch-setting-value-type.sql diff --git a/db/patch-status-page-footer-css.sql b/db/old_migrations/patch-status-page-footer-css.sql similarity index 100% rename from db/patch-status-page-footer-css.sql rename to db/old_migrations/patch-status-page-footer-css.sql diff --git a/db/patch-status-page.sql b/db/old_migrations/patch-status-page.sql similarity index 100% rename from db/patch-status-page.sql rename to db/old_migrations/patch-status-page.sql diff --git a/db/patch1.sql b/db/old_migrations/patch1.sql similarity index 100% rename from db/patch1.sql rename to db/old_migrations/patch1.sql diff --git a/db/patch10.sql b/db/old_migrations/patch10.sql similarity index 100% rename from db/patch10.sql rename to db/old_migrations/patch10.sql diff --git a/db/patch2.sql b/db/old_migrations/patch2.sql similarity index 100% rename from db/patch2.sql rename to db/old_migrations/patch2.sql diff --git a/db/patch3.sql b/db/old_migrations/patch3.sql similarity index 100% rename from db/patch3.sql rename to db/old_migrations/patch3.sql diff --git a/db/patch4.sql b/db/old_migrations/patch4.sql similarity index 100% rename from db/patch4.sql rename to db/old_migrations/patch4.sql diff --git a/db/patch5.sql b/db/old_migrations/patch5.sql similarity index 100% rename from db/patch5.sql rename to db/old_migrations/patch5.sql diff --git a/db/patch6.sql b/db/old_migrations/patch6.sql similarity index 100% rename from db/patch6.sql rename to db/old_migrations/patch6.sql diff --git a/db/patch7.sql b/db/old_migrations/patch7.sql similarity index 100% rename from db/patch7.sql rename to db/old_migrations/patch7.sql diff --git a/db/patch8.sql b/db/old_migrations/patch8.sql similarity index 100% rename from db/patch8.sql rename to db/old_migrations/patch8.sql diff --git a/db/patch9.sql b/db/old_migrations/patch9.sql similarity index 100% rename from db/patch9.sql rename to db/old_migrations/patch9.sql diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile deleted file mode 100644 index ee2090257..000000000 --- a/docker/alpine-base.dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# DON'T UPDATE TO alpine3.13, 1.14, see #41. -FROM node:16-alpine3.12 -WORKDIR /app - -# Install apprise, iputils for non-root ping, setpriv -RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib git && \ - pip3 --no-cache-dir install apprise==1.4.0 && \ - rm -rf /root/.cache diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index 244e6c5fb..2dd8dae99 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -1,12 +1,11 @@ -# DON'T UPDATE TO node:14-bullseye-slim, see #372. # If the image changed, the second stage image should be changed too -FROM node:16-buster-slim +FROM node:20-bookworm-slim AS base2-slim ARG TARGETPLATFORM WORKDIR /app # Specify --no-install-recommends to skip unused dependencies, make the base much smaller! -# python3* = apprise's dependencies +# apprise = for notifications (From testing repo) # sqlite3 = for debugging # iputils-ping = for ping # util-linux = for setpriv (Should be dropped in 2.0.0?) @@ -15,29 +14,25 @@ WORKDIR /app # ca-certificates = keep the cert up-to-date # sudo = for start service nscd with non-root user # nscd = for better DNS caching -# (pip) apprise = for notifications -RUN apt-get update && \ - apt-get --yes --no-install-recommends install \ - python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ - sqlite3 \ +RUN echo "deb http://deb.debian.org/debian testing main" >> /etc/apt/sources.list && \ + apt update && \ + apt --yes --no-install-recommends -t testing install apprise sqlite3 ca-certificates && \ + apt --yes --no-install-recommends -t stable install \ iputils-ping \ util-linux \ dumb-init \ curl \ - ca-certificates \ sudo \ nscd && \ - pip3 --no-cache-dir install apprise==1.4.5 && \ rm -rf /var/lib/apt/lists/* && \ apt --yes autoremove + # Install cloudflared -RUN set -eux && \ - mkdir -p --mode=0755 /usr/share/keyrings && \ - curl --fail --show-error --silent --location --insecure https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyrings/cloudflare-main.gpg && \ - echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared buster main' | tee /etc/apt/sources.list.d/cloudflared.list && \ - apt-get update && \ - apt-get install --yes --no-install-recommends cloudflared && \ +RUN curl https://pkg.cloudflare.com/cloudflare-main.gpg --output /usr/share/keyrings/cloudflare-main.gpg && \ + echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bullseye main' | tee /etc/apt/sources.list.d/cloudflared.list && \ + apt update && \ + apt install --yes --no-install-recommends -t stable cloudflared && \ cloudflared version && \ rm -rf /var/lib/apt/lists/* && \ apt --yes autoremove @@ -46,3 +41,16 @@ RUN set -eux && \ COPY ./docker/etc/nscd.conf /etc/nscd.conf COPY ./docker/etc/sudoers /etc/sudoers + +# Full Base Image +# MariaDB, Chromium and fonts +# Not working for armv7, so use the older version (10.5) of MariaDB from the debian repo +# curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup | bash -s -- --mariadb-server-version="mariadb-11.1" && \ +FROM base2-slim AS base2 +ENV UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB=1 +RUN apt update && \ + apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk mariadb-server && \ + apt --yes remove curl && \ + rm -rf /var/lib/apt/lists/* && \ + apt --yes autoremove && \ + chown -R node:node /var/lib/mysql diff --git a/docker/docker-compose-dev.yml b/docker/docker-compose-dev.yml new file mode 100644 index 000000000..c66b24b58 --- /dev/null +++ b/docker/docker-compose-dev.yml @@ -0,0 +1,14 @@ +version: '3.8' + +services: + uptime-kuma: + container_name: uptime-kuma-dev + image: louislam/uptime-kuma:nightly2 + volumes: + #- ./data:/app/data + - ../server:/app/server + - ../db:/app/db + ports: + - "3001:3001" # : + - "3307:3306" + diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f5c8f3661..20e373292 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,14 +1,15 @@ -# Simple docker-compose.yml -# You can change your port or volume location - -version: '3.3' +version: '3.8' services: uptime-kuma: - image: louislam/uptime-kuma:1 + image: louislam/uptime-kuma:2 container_name: uptime-kuma volumes: - - ./uptime-kuma-data:/app/data + - uptime-kuma:/app/data ports: - - 3001:3001 # : + - "3001:3001" # : restart: always + +volumes: + uptime-kuma: + diff --git a/docker/dockerfile b/docker/dockerfile index 1bc90f929..8c964750e 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -1,6 +1,8 @@ +ARG BASE_IMAGE=louislam/uptime-kuma:base2 + ############################################ # Build in Golang -# Run npm run build-healthcheck-armv7 in the host first, another it will be super slow where it is building the armv7 healthcheck +# Run npm run build-healthcheck-armv7 in the host first, otherwise it will be super slow where it is building the armv7 healthcheck # Check file: builder-go.dockerfile ############################################ FROM louislam/uptime-kuma:builder-go AS build_healthcheck @@ -8,49 +10,47 @@ FROM louislam/uptime-kuma:builder-go AS build_healthcheck ############################################ # Build in Node.js ############################################ -FROM louislam/uptime-kuma:base-debian AS build +FROM louislam/uptime-kuma:base2 AS build +USER node WORKDIR /app ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 -COPY .npmrc .npmrc -COPY package.json package.json -COPY package-lock.json package-lock.json +COPY --chown=node:node .npmrc .npmrc +COPY --chown=node:node package.json package.json +COPY --chown=node:node package-lock.json package-lock.json RUN npm ci --omit=dev COPY . . -COPY --from=build_healthcheck /app/extra/healthcheck /app/extra/healthcheck -RUN chmod +x /app/extra/entrypoint.sh +COPY --chown=node:node --from=build_healthcheck /app/extra/healthcheck /app/extra/healthcheck ############################################ # ⭐ Main Image ############################################ -FROM louislam/uptime-kuma:base-debian AS release +FROM $BASE_IMAGE AS release +USER node WORKDIR /app ENV UPTIME_KUMA_IS_CONTAINER=1 # Copy app files from build layer -COPY --from=build /app /app - +COPY --chown=node:node --from=build /app /app EXPOSE 3001 -VOLUME ["/app/data"] HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD extra/healthcheck -ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"] +ENTRYPOINT ["/usr/bin/dumb-init", "--"] CMD ["node", "server/server.js"] ############################################ # Mark as Nightly ############################################ FROM release AS nightly +USER node RUN npm run mark-as-nightly ############################################ # Build an image for testing pr ############################################ -FROM louislam/uptime-kuma:base-debian AS pr-test - +FROM louislam/uptime-kuma:base2 AS pr-test2 WORKDIR /app - ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 ## Install Git @@ -78,7 +78,7 @@ CMD ["npm", "run", "start-pr-test"] ############################################ # Upload the artifact to Github ############################################ -FROM louislam/uptime-kuma:base-debian AS upload-artifact +FROM louislam/uptime-kuma:base2 AS upload-artifact WORKDIR / RUN apt update && \ apt --yes install curl file diff --git a/docker/dockerfile-alpine b/docker/dockerfile-alpine deleted file mode 100644 index 43f26b8bb..000000000 --- a/docker/dockerfile-alpine +++ /dev/null @@ -1,27 +0,0 @@ -FROM louislam/uptime-kuma:base-alpine AS build -WORKDIR /app - -ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 - -COPY .npmrc .npmrc -COPY package.json package.json -COPY package-lock.json package-lock.json -RUN npm ci --omit=dev -COPY . . -RUN chmod +x /app/extra/entrypoint.sh - -FROM louislam/uptime-kuma:base-alpine AS release -WORKDIR /app - -# Copy app files from build layer -COPY --from=build /app /app - -EXPOSE 3001 -VOLUME ["/app/data"] -HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD node extra/healthcheck.js -ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"] -CMD ["node", "server/server.js"] - - -FROM release AS nightly -RUN npm run mark-as-nightly diff --git a/extra/beta/update-version.js b/extra/beta/update-version.js index 3dafbe8d4..d8e626d03 100644 --- a/extra/beta/update-version.js +++ b/extra/beta/update-version.js @@ -36,6 +36,8 @@ if (! exists) { /** * Commit updated files * @param {string} version Version to update to + * @returns {void} + * @throws Error committing files */ function commit(version) { let msg = "Update to " + version; @@ -55,6 +57,7 @@ function commit(version) { /** * Create a tag with the specified version * @param {string} version Tag to create + * @returns {void} */ function tag(version) { let res = childProcess.spawnSync("git", [ "tag", version ]); @@ -68,6 +71,7 @@ function tag(version) { * Check if a tag exists for the specified version * @param {string} version Version to check * @returns {boolean} Does the tag already exist + * @throws Version is not valid */ function tagExists(version) { if (! version) { diff --git a/extra/download-dist.js b/extra/download-dist.js index a854ca8b2..b8be5eb8a 100644 --- a/extra/download-dist.js +++ b/extra/download-dist.js @@ -15,6 +15,7 @@ download(url); /** * Downloads the latest version of the dist from a GitHub release. * @param {string} url The URL to download from. + * @returns {void} * * Generated by Trelent */ diff --git a/extra/entrypoint.sh b/extra/entrypoint.sh deleted file mode 100644 index 23c4f0177..000000000 --- a/extra/entrypoint.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env sh - -# set -e Exit the script if an error happens -set -e -PUID=${PUID=0} -PGID=${PGID=0} - -files_ownership () { - # -h Changes the ownership of an encountered symbolic link and not that of the file or directory pointed to by the symbolic link. - # -R Recursively descends the specified directories - # -c Like verbose but report only when a change is made - chown -hRc "$PUID":"$PGID" /app/data -} - -echo "==> Performing startup jobs and maintenance tasks" -files_ownership - -echo "==> Starting application with user $PUID group $PGID" - -# --clear-groups Clear supplementary groups. -exec setpriv --reuid "$PUID" --regid "$PGID" --clear-groups "$@" diff --git a/extra/fs-rmSync.js b/extra/fs-rmSync.js index aa45b6dc3..0fdbab936 100644 --- a/extra/fs-rmSync.js +++ b/extra/fs-rmSync.js @@ -4,12 +4,12 @@ const fs = require("fs"); * to avoid the runtime deprecation warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16, * or the `recursive` property removing completely in the future Node.js version. * See the link below. - * * @todo Once we drop the support for Node.js v14 (or at least versions before v14.14.0), we can safely replace this function with `fs.rmSync`, since `fs.rmSync` was add in Node.js v14.14.0 and currently we supports all the Node.js v14 versions that include the versions before the v14.14.0, and this function have almost the same signature with `fs.rmSync`. * @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- the deprecation infomation of `fs.rmdirSync` * @link https://nodejs.org/docs/latest-v16.x/api/fs.html#fsrmsyncpath-options the document of `fs.rmSync` * @param {fs.PathLike} path Valid types for path values in "fs". - * @param {fs.RmDirOptions} [options] options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`. + * @param {fs.RmDirOptions} options options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`. + * @returns {void} */ const rmSync = (path, options) => { if (typeof fs.rmSync === "function") { diff --git a/extra/remove-2fa.js b/extra/remove-2fa.js index f88c43fca..e6a8b97c8 100644 --- a/extra/remove-2fa.js +++ b/extra/remove-2fa.js @@ -12,7 +12,7 @@ const rl = readline.createInterface({ }); const main = async () => { - Database.init(args); + Database.initDataDir(args); await Database.connect(); try { diff --git a/extra/reset-password.js b/extra/reset-password.js index 168983312..3f6f79c1d 100644 --- a/extra/reset-password.js +++ b/extra/reset-password.js @@ -13,7 +13,7 @@ const rl = readline.createInterface({ const main = async () => { console.log("Connecting the database"); - Database.init(args); + Database.initDataDir(args); await Database.connect(false, false, true); try { diff --git a/extra/simple-dns-server.js b/extra/simple-dns-server.js index a6946dcb3..38bf74f68 100644 --- a/extra/simple-dns-server.js +++ b/extra/simple-dns-server.js @@ -138,7 +138,7 @@ server.listen({ /** * Get human readable request type from request code * @param {number} code Request code to translate - * @returns {string} Human readable request type + * @returns {string|void} Human readable request type */ function type(code) { for (let name in Packet.TYPE) { diff --git a/extra/simple-mqtt-server.js b/extra/simple-mqtt-server.js index b970a380e..66ebfe4d9 100644 --- a/extra/simple-mqtt-server.js +++ b/extra/simple-mqtt-server.js @@ -7,11 +7,17 @@ class SimpleMqttServer { aedes = require("aedes")(); server = require("net").createServer(this.aedes.handle); + /** + * @param {number} port Port to listen on + */ constructor(port) { this.port = port; } - /** Start the MQTT server */ + /** + * Start the MQTT server + * @returns {void} + */ start() { this.server.listen(this.port, () => { console.log("server started and listening on port ", this.port); diff --git a/extra/update-language-files/index.js b/extra/update-language-files/index.js index 078c4e6f8..5b748a98e 100644 --- a/extra/update-language-files/index.js +++ b/extra/update-language-files/index.js @@ -12,6 +12,7 @@ import rmSync from "../fs-rmSync.js"; * created with this code if one does not already exist * @param {string} baseLang The second base language file to copy. This * will be ignored if set to "en" as en.js is copied by default + * @returns {void} */ function copyFiles(langCode, baseLang) { if (fs.existsSync("./languages")) { @@ -33,7 +34,8 @@ function copyFiles(langCode, baseLang) { /** * Update the specified language file * @param {string} langCode Language code to update - * @param {string} baseLang Second language to copy keys from + * @param {string} baseLangCode Second language to copy keys from + * @returns {void} */ async function updateLanguage(langCode, baseLangCode) { const en = (await import("./languages/en.js")).default; diff --git a/extra/update-version.js b/extra/update-version.js index 8d78f17db..2bc13a78e 100644 --- a/extra/update-version.js +++ b/extra/update-version.js @@ -39,6 +39,8 @@ if (! exists) { /** * Commit updated files * @param {string} version Version to update to + * @returns {void} + * @throws Error when committing files */ function commit(version) { let msg = "Update to " + version; @@ -55,6 +57,7 @@ function commit(version) { /** * Create a tag with the specified version * @param {string} version Tag to create + * @returns {void} */ function tag(version) { let res = childProcess.spawnSync("git", [ "tag", version ]); @@ -65,6 +68,7 @@ function tag(version) { * Check if a tag exists for the specified version * @param {string} version Version to check * @returns {boolean} Does the tag already exist + * @throws Version is not valid */ function tagExists(version) { if (! version) { diff --git a/extra/update-wiki-version.js b/extra/update-wiki-version.js index f551db41b..003deec01 100644 --- a/extra/update-wiki-version.js +++ b/extra/update-wiki-version.js @@ -13,6 +13,7 @@ updateWiki(newVersion); /** * Update the wiki with new version number * @param {string} newVersion Version to update to + * @returns {void} */ function updateWiki(newVersion) { const wikiDir = "./tmp/wiki"; @@ -46,6 +47,7 @@ function updateWiki(newVersion) { /** * Check if a directory exists and then delete it * @param {string} dir Directory to delete + * @returns {void} */ function safeDelete(dir) { if (fs.existsSync(dir)) { diff --git a/package-lock.json b/package-lock.json index 1ffec26ca..96c9eef8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.23.1", + "version": "1.23.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.23.1", + "version": "1.23.0", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", @@ -43,6 +43,7 @@ "jsonwebtoken": "~9.0.0", "jwt-decode": "~3.1.2", "kafkajs": "^2.2.4", + "knex": "^2.4.2", "limiter": "~2.1.0", "liquidjs": "^10.7.0", "mongodb": "~4.14.0", @@ -102,6 +103,7 @@ "dns2": "~2.0.1", "dompurify": "~2.4.3", "eslint": "~8.14.0", + "eslint-plugin-jsdoc": "^46.4.6", "eslint-plugin-vue": "~8.7.1", "favico.js": "~0.3.10", "jest": "~29.6.1", @@ -117,6 +119,7 @@ "stylelint": "^15.10.1", "stylelint-config-standard": "~25.0.0", "terser": "~5.15.0", + "test": "~3.3.0", "timezones-list": "~3.0.1", "typescript": "~4.4.4", "v-pagination-3": "~0.1.7", @@ -142,15 +145,6 @@ "node": "14 || 16 || 18 || >= 20.4.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@actions/github": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz", @@ -164,9 +158,9 @@ } }, "node_modules/@actions/http-client": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz", - "integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz", + "integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==", "dev": true, "dependencies": { "tunnel": "^0.0.6" @@ -288,47 +282,60 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "optional": true }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.357.0.tgz", + "integrity": "sha512-nQYDJon87quPwt2JZJwUN2GFKJnvE5kWb6tZP4xb5biSGUKBqDQo06oYed7yokatCuCMouIXV462aN0fWODtOw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.398.0.tgz", - "integrity": "sha512-Pr/S1f8R2FsJ8DwBC6g0CSdtZNNV5dMHhlIi+t8YAmCJvP4KT+UhzFjbvQRINlBRLFuGUuP7p5vRcGVELD3+wA==", + "version": "3.359.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.359.0.tgz", + "integrity": "sha512-zb5hSVuyHOXFTjGiqzPhQ/F6Zg4oLffO/NmC3MyvufUzr8yZYmcQzxNU6Jv6WbVmP01OiU4KAozBLMS7URfgzg==", "optional": true, "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/client-sts": "3.359.0", + "@aws-sdk/config-resolver": "3.357.0", + "@aws-sdk/credential-provider-node": "3.358.0", + "@aws-sdk/fetch-http-handler": "3.357.0", + "@aws-sdk/hash-node": "3.357.0", + "@aws-sdk/invalid-dependency": "3.357.0", + "@aws-sdk/middleware-content-length": "3.357.0", + "@aws-sdk/middleware-endpoint": "3.357.0", + "@aws-sdk/middleware-host-header": "3.357.0", + "@aws-sdk/middleware-logger": "3.357.0", + "@aws-sdk/middleware-recursion-detection": "3.357.0", + "@aws-sdk/middleware-retry": "3.357.0", + "@aws-sdk/middleware-serde": "3.357.0", + "@aws-sdk/middleware-signing": "3.357.0", + "@aws-sdk/middleware-stack": "3.357.0", + "@aws-sdk/middleware-user-agent": "3.357.0", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/node-http-handler": "3.357.0", + "@aws-sdk/smithy-client": "3.358.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.358.0", + "@aws-sdk/util-defaults-mode-node": "3.358.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-retry": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.357.0", + "@aws-sdk/util-user-agent-node": "3.357.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", "tslib": "^2.5.0" }, "engines": { @@ -336,43 +343,87 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.398.0.tgz", - "integrity": "sha512-CygL0jhfibw4kmWXG/3sfZMFNjcXo66XUuPC4BqZBk8Rj5vFoxp1vZeMkDLzTIk97Nvo5J5Bh+QnXKhub6AckQ==", + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.358.0.tgz", + "integrity": "sha512-Kc9IsoPIHJfkjDuStyItwQAOpnxw/I9xfF3vvukeN9vkXcRiWeMDhEXACN4L1AYFlU9FHQSRdNwpYTIz7OrD2A==", "optional": true, "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/config-resolver": "3.357.0", + "@aws-sdk/fetch-http-handler": "3.357.0", + "@aws-sdk/hash-node": "3.357.0", + "@aws-sdk/invalid-dependency": "3.357.0", + "@aws-sdk/middleware-content-length": "3.357.0", + "@aws-sdk/middleware-endpoint": "3.357.0", + "@aws-sdk/middleware-host-header": "3.357.0", + "@aws-sdk/middleware-logger": "3.357.0", + "@aws-sdk/middleware-recursion-detection": "3.357.0", + "@aws-sdk/middleware-retry": "3.357.0", + "@aws-sdk/middleware-serde": "3.357.0", + "@aws-sdk/middleware-stack": "3.357.0", + "@aws-sdk/middleware-user-agent": "3.357.0", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/node-http-handler": "3.357.0", + "@aws-sdk/smithy-client": "3.358.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.358.0", + "@aws-sdk/util-defaults-mode-node": "3.358.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-retry": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.357.0", + "@aws-sdk/util-user-agent-node": "3.357.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.358.0.tgz", + "integrity": "sha512-Gy09fSlhJdGbr8rNNR8EdLaUynB1B34nw8kN1aFT4CdAnjFKxTainqG6Aq4vx64TbMDMhvMYWpNAluvq7UHVhw==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.357.0", + "@aws-sdk/fetch-http-handler": "3.357.0", + "@aws-sdk/hash-node": "3.357.0", + "@aws-sdk/invalid-dependency": "3.357.0", + "@aws-sdk/middleware-content-length": "3.357.0", + "@aws-sdk/middleware-endpoint": "3.357.0", + "@aws-sdk/middleware-host-header": "3.357.0", + "@aws-sdk/middleware-logger": "3.357.0", + "@aws-sdk/middleware-recursion-detection": "3.357.0", + "@aws-sdk/middleware-retry": "3.357.0", + "@aws-sdk/middleware-serde": "3.357.0", + "@aws-sdk/middleware-stack": "3.357.0", + "@aws-sdk/middleware-user-agent": "3.357.0", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/node-http-handler": "3.357.0", + "@aws-sdk/smithy-client": "3.358.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.358.0", + "@aws-sdk/util-defaults-mode-node": "3.358.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-retry": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.357.0", + "@aws-sdk/util-user-agent-node": "3.357.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", "tslib": "^2.5.0" }, "engines": { @@ -380,46 +431,46 @@ } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.398.0.tgz", - "integrity": "sha512-/3Pa9wLMvBZipKraq3AtbmTfXW6q9kyvhwOno64f1Fz7kFb8ijQFMGoATS70B2pGEZTlxkUqJFWDiisT6Q6dFg==", + "version": "3.359.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.359.0.tgz", + "integrity": "sha512-zpyui8hXvEUvq8MwzZsm51ni0intvPjtV8dgx10nVJnm605nqrLlAMGqQ1S/UxO7CVmhqWbh5dnGHEc//UJlsw==", "optional": true, "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-sdk-sts": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/config-resolver": "3.357.0", + "@aws-sdk/credential-provider-node": "3.358.0", + "@aws-sdk/fetch-http-handler": "3.357.0", + "@aws-sdk/hash-node": "3.357.0", + "@aws-sdk/invalid-dependency": "3.357.0", + "@aws-sdk/middleware-content-length": "3.357.0", + "@aws-sdk/middleware-endpoint": "3.357.0", + "@aws-sdk/middleware-host-header": "3.357.0", + "@aws-sdk/middleware-logger": "3.357.0", + "@aws-sdk/middleware-recursion-detection": "3.357.0", + "@aws-sdk/middleware-retry": "3.357.0", + "@aws-sdk/middleware-sdk-sts": "3.357.0", + "@aws-sdk/middleware-serde": "3.357.0", + "@aws-sdk/middleware-signing": "3.357.0", + "@aws-sdk/middleware-stack": "3.357.0", + "@aws-sdk/middleware-user-agent": "3.357.0", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/node-http-handler": "3.357.0", + "@aws-sdk/smithy-client": "3.358.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-body-length-browser": "3.310.0", + "@aws-sdk/util-body-length-node": "3.310.0", + "@aws-sdk/util-defaults-mode-browser": "3.358.0", + "@aws-sdk/util-defaults-mode-node": "3.358.0", + "@aws-sdk/util-endpoints": "3.357.0", + "@aws-sdk/util-retry": "3.357.0", + "@aws-sdk/util-user-agent-browser": "3.357.0", + "@aws-sdk/util-user-agent-node": "3.357.0", + "@aws-sdk/util-utf8": "3.310.0", + "@smithy/protocol-http": "^1.0.1", + "@smithy/types": "^1.0.0", "fast-xml-parser": "4.2.5", "tslib": "^2.5.0" }, @@ -427,16 +478,30 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/credential-provider-cognito-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.398.0.tgz", - "integrity": "sha512-MFUhy1YayHg5ypRTk4OTfDumQRP+OJBagaGv14kA8DzhKH1sNrU4HV7A7y2J4SvkN5hG/KnLJqxpakCtB2/O2g==", + "node_modules/@aws-sdk/config-resolver": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.357.0.tgz", + "integrity": "sha512-cukfg0nX7Tzx/xFyH5F4Eyb8DA1ITCGtSQv4vnEjgUop+bkzckuGLKEeBcBhyZY+aw+2C9CVwIHwIMhRm0ul5w==", "optional": true, "dependencies": { - "@aws-sdk/client-cognito-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-config-provider": "3.310.0", + "@aws-sdk/util-middleware": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.359.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.359.0.tgz", + "integrity": "sha512-dSuHTucXcjIFsjdOq0HeSk0niWJ7V2hWnwyYh7MCwv43dP9u4V+11boLC6zIrw2Epx++JnIqhggKJAi6l/occw==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.359.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -444,14 +509,29 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.398.0.tgz", - "integrity": "sha512-Z8Yj5z7FroAsR6UVML+XUdlpoqEe9Dnle8c2h8/xWwIC2feTfIBhjLhRVxfbpbM1pLgBSNEcZ7U8fwq5l7ESVQ==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.357.0.tgz", + "integrity": "sha512-UOecwfqvXgJVqhfWSZ2S44v2Nq2oceW0PQVQp0JAa9opc2rxSVIfyOhPr0yMoPmpyNcP22rgeg6ce70KULYwiA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.357.0.tgz", + "integrity": "sha512-upw/bfsl7/WydT6gM0lBuR4Ipp4fzYm/E3ObFr0Mg5OkgVPt5ZJE+eeFTvwCpDdBSTKs4JfrK6/iEK8A23Q1jQ==", + "optional": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -459,20 +539,19 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.398.0.tgz", - "integrity": "sha512-AsK1lStK3nB9Cn6S6ODb1ktGh7SRejsNVQVKX3t5d3tgOaX+aX1Iwy8FzM/ZEN8uCloeRifUGIY9uQFygg5mSw==", + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.358.0.tgz", + "integrity": "sha512-Blmw4bhGxpaYvPmrbRKAltqnNDDSf6ZegNqJasc5OWvAlHJNvB/hYPmyQN0oFy79BXn7PbBip1QaLWaEhJvpAA==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/credential-provider-env": "3.357.0", + "@aws-sdk/credential-provider-imds": "3.357.0", + "@aws-sdk/credential-provider-process": "3.357.0", + "@aws-sdk/credential-provider-sso": "3.358.0", + "@aws-sdk/credential-provider-web-identity": "3.357.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/shared-ini-file-loader": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -480,21 +559,20 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.398.0.tgz", - "integrity": "sha512-odmI/DSKfuWUYeDnGTCEHBbC8/MwnF6yEq874zl6+owoVv0ZsYP8qBHfiJkYqrwg7wQ7Pi40sSAPC1rhesGwzg==", + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.358.0.tgz", + "integrity": "sha512-iLjyRNOT0ycdLqkzXNW+V2zibVljkLjL8j45FpK6mNrAwc/Ynr7EYuRRp5OuRiiYDO3ZoneAxpBJQ5SqmK2Jfg==", "optional": true, "dependencies": { - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-ini": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/credential-provider-env": "3.357.0", + "@aws-sdk/credential-provider-imds": "3.357.0", + "@aws-sdk/credential-provider-ini": "3.358.0", + "@aws-sdk/credential-provider-process": "3.357.0", + "@aws-sdk/credential-provider-sso": "3.358.0", + "@aws-sdk/credential-provider-web-identity": "3.357.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/shared-ini-file-loader": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -502,15 +580,14 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.398.0.tgz", - "integrity": "sha512-WrkBL1W7TXN508PA9wRXPFtzmGpVSW98gDaHEaa8GolAPHMPa5t2QcC/z/cFpglzrcVv8SA277zu9Z8tELdZhg==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.357.0.tgz", + "integrity": "sha512-qFWWilFPsc2hR7O0KIhwcE78w+pVIK+uQR6MQMfdRyxUndgiuCorJwVjedc3yZtmnoELHF34j+m8whTBXv9E7Q==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/shared-ini-file-loader": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -518,17 +595,16 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.398.0.tgz", - "integrity": "sha512-2Dl35587xbnzR/GGZqA2MnFs8+kS4wbHQO9BioU0okA+8NRueohNMdrdQmQDdSNK4BfIpFspiZmFkXFNyEAfgw==", + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.358.0.tgz", + "integrity": "sha512-hKu5NshKohSDoHaXKyeCW88J8dBt4TMljrL+WswTMifuThO9ptyMq4PCdl4z7CNjIq6zo3ftc/uNf8TY7Ga8+w==", "optional": true, "dependencies": { - "@aws-sdk/client-sso": "3.398.0", - "@aws-sdk/token-providers": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/client-sso": "3.358.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/shared-ini-file-loader": "3.357.0", + "@aws-sdk/token-providers": "3.358.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -536,14 +612,13 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.398.0.tgz", - "integrity": "sha512-iG3905Alv9pINbQ8/MIsshgqYMbWx+NDQWpxbIW3W0MkSH3iAqdVpSCteYidYX9G/jv2Um1nW3y360ib20bvNg==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.357.0.tgz", + "integrity": "sha512-0KRRAFrXy5HJe2vqnCWCoCS+fQw7IoIj3KQsuURJMW4F+ifisxCgEsh3brJ2LQlN4ElWTRJhlrDHNZ/pd61D4w==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -551,25 +626,116 @@ } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.398.0.tgz", - "integrity": "sha512-355vXmImn2e85mIWSYDVb101AF2lIVHKNCaH6sV1U/8i0ZOXh2cJYNdkRYrxNt1ezDB0k97lSKvuDx7RDvJyRg==", + "version": "3.359.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.359.0.tgz", + "integrity": "sha512-fwfdqoJihRUbk3KEYv8IfWRFI+cNQfXfVHLtDEcW3tCU8lqsL920YSEjqMuWGrWLp8dWESDX5C3wZugur0lnTQ==", "optional": true, "dependencies": { - "@aws-sdk/client-cognito-identity": "3.398.0", - "@aws-sdk/client-sso": "3.398.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-cognito-identity": "3.398.0", - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-ini": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/client-cognito-identity": "3.359.0", + "@aws-sdk/client-sso": "3.358.0", + "@aws-sdk/client-sts": "3.359.0", + "@aws-sdk/credential-provider-cognito-identity": "3.359.0", + "@aws-sdk/credential-provider-env": "3.357.0", + "@aws-sdk/credential-provider-imds": "3.357.0", + "@aws-sdk/credential-provider-ini": "3.358.0", + "@aws-sdk/credential-provider-node": "3.358.0", + "@aws-sdk/credential-provider-process": "3.357.0", + "@aws-sdk/credential-provider-sso": "3.358.0", + "@aws-sdk/credential-provider-web-identity": "3.357.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-codec": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.357.0.tgz", + "integrity": "sha512-bqenTHG6GH6aCk/Il+ooWXVVAZuc8lOgVEy9bE2hI49oVqT8zSuXxQB+w1WWyZoAOPcelsjayB1wfPub8VDBxQ==", + "optional": true, + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.357.0.tgz", + "integrity": "sha512-5sPloTO8y8fAnS/6/Sfp/aVoL9zuhzkLdWBORNzMazdynVNEzWKWCPZ27RQpgkaCDHiXjqUY4kfuFXAGkvFfDQ==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/querystring-builder": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.357.0.tgz", + "integrity": "sha512-fq3LS9AxHKb7dTZkm6iM1TrGk6XOTZz96iEZPME1+vjiSEXGWuebHt87q92n+KozVGRypn9MId3lHOPBBjygNQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.357.0.tgz", + "integrity": "sha512-HnCYZczf0VdyxMVMMxmA3QJAyyPSFbcMtZzgKbxVTWTG7GKpQe0psWZu/7O2Nk31mKg6vEUdiP1FylqLBsgMOA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.310.0.tgz", + "integrity": "sha512-urnbcCR+h9NWUnmOtet/s4ghvzsidFmspfhYaHAmSRdy9yDjdjBJMFjjsn85A1ODUktztm+cVncXjQ38WCMjMQ==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.357.0.tgz", + "integrity": "sha512-zQOFEyzOXAgN4M54tYNWGxKxnyzY0WwYDTFzh9riJRmxN1hTEKHUKmze4nILIf5rkQmOG4kTf1qmfazjkvZAhw==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.357.0.tgz", + "integrity": "sha512-ScJi0SL8X/Lyi0Fp5blg0QN/Z6PoRwV/ZJXd8dQkXSznkbSvJHfqPP0xk/w3GcQ1TKsu5YEPfeYy8ejcq+7Pgg==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-serde": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/url-parser": "3.357.0", + "@aws-sdk/util-middleware": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -577,14 +743,13 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.398.0.tgz", - "integrity": "sha512-m+5laWdBaxIZK2ko0OwcCHJZJ5V1MgEIt8QVQ3k4/kOkN9ICjevOYmba751pHoTnbOYB7zQd6D2OT3EYEEsUcA==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.357.0.tgz", + "integrity": "sha512-HuGLcP7JP1qJ5wGT9GSlEknDaTSnOzHY4T6IPFuvFjAy3PvY5siQNm6+VRqdVS+n6/kzpL3JP5sAVM3aoxHT6Q==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -592,13 +757,12 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.398.0.tgz", - "integrity": "sha512-CiJjW+FL12elS6Pn7/UVjVK8HWHhXMfvHZvOwx/Qkpy340sIhkuzOO6fZEruECDTZhl2Wqn81XdJ1ZQ4pRKpCg==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.357.0.tgz", + "integrity": "sha512-dncT3tr+lZ9+duZo52rASgO6AKVwRcsc2/T93gmaYVrJqI6WWAwQ7yML5s72l9ZjQ5LZ+4jjrgtlufavAS0eCg==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -606,29 +770,58 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.398.0.tgz", - "integrity": "sha512-7QpOqPQAZNXDXv6vsRex4R8dLniL0E/80OPK4PPFsrCh9btEyhN9Begh4i1T+5lL28hmYkztLOkTQ2N5J3hgRQ==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.357.0.tgz", + "integrity": "sha512-AXC54IeDS3jC1dbbkYHML4STvBPcKZ4IJTWdjEK1RCOgqXd0Ze1cE1e21wyj1tM6prF03zLyvpBd+3TS++nqfA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/middleware-sdk-sts": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.398.0.tgz", - "integrity": "sha512-+JH76XHEgfVihkY+GurohOQ5Z83zVN1nYcQzwCFnCDTh4dG4KwhnZKG+WPw6XJECocY0R+H0ivofeALHvVWJtQ==", + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.357.0.tgz", + "integrity": "sha512-ZCbXCYv3nglQqwREYxxpclrnR9MYPAnHlLcC8e9PbApqxGnaZdhoywxoqbgqT3hf/RM7kput4vEHDl1fyymcRQ==", "optional": true, "dependencies": { - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/service-error-classification": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-middleware": "3.357.0", + "@aws-sdk/util-retry": "3.357.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.357.0.tgz", + "integrity": "sha512-Ng2VjLrPiL02QOcs1qs9jG2boO4Gn+v3VIbOJLG4zXcfbSq55iIWtlmr2ljfw9vP5aLhWtcODfmKHS5Bp+019Q==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.357.0.tgz", + "integrity": "sha512-bGI4kYuuEsFjlANbyJLyy4AovETnyf/SukgLOG7Qjbua+ZGuzvRhMsk21mBKKGrnsTO4PmtieJo6xClThGAN8g==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -636,17 +829,28 @@ } }, "node_modules/@aws-sdk/middleware-signing": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.398.0.tgz", - "integrity": "sha512-O0KqXAix1TcvZBFt1qoFkHMUNJOSgjJTYS7lFTRKSwgsD27bdW2TM2r9R8DAccWFt5Amjkdt+eOwQMIXPGTm8w==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.357.0.tgz", + "integrity": "sha512-yB9ewEqI6Fw1OrmKFrUypbCqN5ijk06UGPochybamMuPxxkwMT3bnrm7eezsCA+TZbJyKhpffpyobwuv+xGNrA==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/signature-v4": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-middleware": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.357.0.tgz", + "integrity": "sha512-nNV+jfwGwmbOGZujAY/U8AW3EbVlxa9DJDLz3TPp/39o6Vu5KEzHJyDDNreo2k9V/TMvV+nOzHafufgPdagv7w==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-middleware": "^2.0.0", "tslib": "^2.5.0" }, "engines": { @@ -654,15 +858,155 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.398.0.tgz", - "integrity": "sha512-nF1jg0L+18b5HvTcYzwyFgfZQQMELJINFqI0mi4yRKaX7T5a3aGp5RVLGGju/6tAGTuFbfBoEhkhU3kkxexPYQ==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.357.0.tgz", + "integrity": "sha512-M/CsAXjGblZS4rEbMb0Dn9IXbfq4EjVaTHBfvuILU/dKRppWvjnm2lRtqCZ+LIT3ATbAjA3/dY7dWsjxQWwijA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-endpoints": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.357.0.tgz", + "integrity": "sha512-kwBIzKCaW3UWqLdELhy7TcN8itNMOjbzga530nalFILMvn2IxrkdKQhNgxGBXy6QK6kCOtH6OmcrG3/oZkLwig==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/shared-ini-file-loader": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.357.0.tgz", + "integrity": "sha512-uoab4xIJux+Q9hQ9A/vWEAjojtBQ0U4K7xEQVa0BXEv7MHH5zv51H+VtrelU1Ed6hsHq4Sx0bxBMFpbbWhNyjA==", + "optional": true, + "dependencies": { + "@aws-sdk/abort-controller": "3.357.0", + "@aws-sdk/protocol-http": "3.357.0", + "@aws-sdk/querystring-builder": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.357.0.tgz", + "integrity": "sha512-im4W0u8WaYxG7J7ko4Xl3OEzK3Mrm1Rz6/txTGe6hTIHlyUISu1ekOQJXK6XYPqNMn8v1G3BiQREoRXUEJFbHg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.357.0.tgz", + "integrity": "sha512-w1JHiI50VEea7duDeAspUiKJmmdIQblvRyjVMOqWA6FIQAyDVuEiPX7/MdQr0ScxhtRQxHbP0I4MFyl7ctRQvA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.357.0.tgz", + "integrity": "sha512-aQcicqB6Y2cNaXPPwunz612a01SMiQQPsdz632F/3Lzn0ua82BJKobHOtaiTUlmVJ5Q4/EAeNfwZgL7tTUNtDQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.357.0.tgz", + "integrity": "sha512-Svvq+atRNP9s2VxiklcUNgCzmt3T5kfs7X2C+yjmxHvOQTPjLNaNGbfC/vhjOK7aoXw0h+lBac48r5ymx1PbQA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.357.0.tgz", + "integrity": "sha512-VuXeL4g5vKO9HjgCZlxmH8Uv1FcqUSjmbPpQkbNtYIDck6u0qzM0rG+n0/1EjyQbPSr3MhW/pkWs5nx2Nljlyg==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.357.0.tgz", + "integrity": "sha512-ceyqM4XxQe0Plb/oQAD2t1UOV2Iy4PFe1oAGM8dfJzYrRKu7zvMwru7/WaB3NYq+/mIY6RU+jjhRmjQ3GySVqA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.357.0.tgz", + "integrity": "sha512-itt4/Jh9FqnzK30qIjXFBvM4J7zN4S/AAqsRMnaX7U4f/MV+1YxQHmzimpdMnsCXXs2jqFqKVRu6DewxJ3nbxg==", + "optional": true, + "dependencies": { + "@aws-sdk/eventstream-codec": "3.357.0", + "@aws-sdk/is-array-buffer": "3.310.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-middleware": "3.357.0", + "@aws-sdk/util-uri-escape": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.358.0.tgz", + "integrity": "sha512-oqctxWb9yAqCh4ENwUkt9MC01l5uKoy+QCiSUUhQ76k7R3lyGOge9ycyRyoKl+oZWvEpnjZevXQFqEfGzkL7bA==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-stack": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-stream": "3.358.0", + "@smithy/types": "^1.0.0", "tslib": "^2.5.0" }, "engines": { @@ -670,45 +1014,15 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.398.0.tgz", - "integrity": "sha512-nrYgjzavGCKJL/48Vt0EL+OlIc5UZLfNGpgyUW9cv3XZwl+kXV0QB+HH0rHZZLfpbBgZ2RBIJR9uD5ieu/6hpQ==", + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.358.0.tgz", + "integrity": "sha512-vATKNCwNhCSo2LzvtkIzW9Yp2/aKNR032VPtIWlDtWGGFhkzGi4FPS0VTdfefxz4rqPWfBz53mh54d9xylsWVw==", "optional": true, "dependencies": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@aws-sdk/client-sso-oidc": "3.358.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/shared-ini-file-loader": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -716,25 +1030,138 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", - "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.357.0.tgz", + "integrity": "sha512-/riCRaXg3p71BeWnShrai0y0QTdXcouPSM0Cn1olZbzTf7s71aLEewrc96qFrL70XhY4XvnxMpqQh+r43XIL3g==", "optional": true, "dependencies": { - "@smithy/types": "^2.2.2", "tslib": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.398.0.tgz", - "integrity": "sha512-Fy0gLYAei/Rd6BrXG4baspCnWTUSd0NdokU1pZh4KlfEAEN1i8SPPgfiO5hLk7+2inqtCmqxVJlfqbMVe9k4bw==", + "node_modules/@aws-sdk/url-parser": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.357.0.tgz", + "integrity": "sha512-fAaU6cFsaAba01lCRsRJiYR/LfXvX2wudyEyutBVglE4dWSoSeu3QJNxImIzTBULfbiFhz59++NQ1JUVx88IVg==", + "optional": true, + "dependencies": { + "@aws-sdk/querystring-parser": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-base64": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.310.0.tgz", + "integrity": "sha512-v3+HBKQvqgdzcbL+pFswlx5HQsd9L6ZTlyPVL2LS9nNXnCcR3XgGz9jRskikRUuUvUXtkSG1J88GAOnJ/apTPg==", + "optional": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.310.0.tgz", + "integrity": "sha512-sxsC3lPBGfpHtNTUoGXMQXLwjmR0zVpx0rSvzTPAuoVILVsp5AU/w5FphNPxD5OVIjNbZv9KsKTuvNTiZjDp9g==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.310.0.tgz", + "integrity": "sha512-2tqGXdyKhyA6w4zz7UPoS8Ip+7sayOg9BwHNidiGm2ikbDxm1YrCfYXvCBdwaJxa4hJfRVz+aL9e+d3GqPI9pQ==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.310.0.tgz", + "integrity": "sha512-i6LVeXFtGih5Zs8enLrt+ExXY92QV25jtEnTKHsmlFqFAuL3VBeod6boeMXkN2p9lbSVVQ1sAOOYZOHYbYkntw==", + "optional": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.310.0.tgz", + "integrity": "sha512-xIBaYo8dwiojCw8vnUcIL4Z5tyfb1v3yjqyJKJWV/dqKUFOOS0U591plmXbM+M/QkXyML3ypon1f8+BoaDExrg==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.358.0.tgz", + "integrity": "sha512-KGfw64wRL/gROLD4Gatda8cUsaNKNhSnx+yDDcG2WkFlFfLr6FHvTijpRxvIM2Jau2ZhcdGzbegLjsFxviTJAA==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.358.0.tgz", + "integrity": "sha512-2C5on0yppDS0xGpFkHRqfrG9TeTq6ive1hPX1V8UCkiI/TBQYl88XCKCKct8zTcejyK9klZUDGI8QQTan2UWkw==", + "optional": true, + "dependencies": { + "@aws-sdk/config-resolver": "3.357.0", + "@aws-sdk/credential-provider-imds": "3.357.0", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/property-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.357.0.tgz", + "integrity": "sha512-XHKyS5JClT9su9hDif715jpZiWHQF9gKZXER8tW0gOizU3R9cyWc9EsJ2BRhFNhi7nt/JF/CLUEc5qDx3ETbUw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.310.0.tgz", + "integrity": "sha512-sVN7mcCCDSJ67pI1ZMtk84SKGqyix6/0A1Ab163YKn+lFBQRMKexleZzpYzNGxYzmQS6VanP/cfU7NiLQOaSfA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", "tslib": "^2.5.0" }, "engines": { @@ -753,27 +1180,81 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.398.0.tgz", - "integrity": "sha512-A3Tzx1tkDHlBT+IgxmsMCHbV8LM7SwwCozq2ZjJRx0nqw3MCrrcxQFXldHeX/gdUMO+0Oocb7HGSnVODTq+0EA==", + "node_modules/@aws-sdk/util-middleware": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.357.0.tgz", + "integrity": "sha512-pV1krjZs7BdahZBfsCJMatE8kcor7GFsBOWrQgQDm9T0We5b5xPpOO2vxAD0RytBpY8Ky2ELs/+qXMv7l5fWIA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-retry": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.357.0.tgz", + "integrity": "sha512-SUqYJE9msbuOVq+vnUy+t0LH7XuYNFz66dSF8q6tedsbJK4j8tgya0I1Ct3m06ynGrXDJMaj39I7AXCyW9bjtw==", + "optional": true, + "dependencies": { + "@aws-sdk/service-error-classification": "3.357.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/util-stream": { + "version": "3.358.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-stream/-/util-stream-3.358.0.tgz", + "integrity": "sha512-zUhpjxAXV2+0eALlTU6uXRYMs10XYpcYzl3NtLRe4wWgnrOOOZnF/t5LQDoKXOfaMdzwZ+i90+PYr+6JQ58+7g==", + "optional": true, + "dependencies": { + "@aws-sdk/fetch-http-handler": "3.357.0", + "@aws-sdk/node-http-handler": "3.357.0", + "@aws-sdk/types": "3.357.0", + "@aws-sdk/util-base64": "3.310.0", + "@aws-sdk/util-buffer-from": "3.310.0", + "@aws-sdk/util-hex-encoding": "3.310.0", + "@aws-sdk/util-utf8": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.310.0.tgz", + "integrity": "sha512-drzt+aB2qo2LgtDoiy/3sVG8w63cgLkqFIa2NFlGpUgHFWTXkqtbgf4L5QdjRGKWhmZsnqkbtL7vkSWEcYDJ4Q==", + "optional": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.357.0.tgz", + "integrity": "sha512-JHaWlNIUkPNvXkqeDOrqFzAlAgdwZK5mZw7FQnCRvf8tdSogpGZSkuyb9Z6rLD9gC40Srbc2nepO1cFpeMsDkA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.357.0", "bowser": "^2.11.0", "tslib": "^2.5.0" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.398.0.tgz", - "integrity": "sha512-RTVQofdj961ej4//fEkppFf4KXqKGMTCqJYghx3G0C/MYXbg7MGl7LjfNGtJcboRE8pfHHQ/TUWBDA7RIAPPlQ==", + "version": "3.357.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.357.0.tgz", + "integrity": "sha512-RdpQoaJWQvcS99TVgSbT451iGrlH4qpWUWFA9U1IRhxOSsmC1hz8ME7xc8nci9SREx/ZlfT3ai6LpoAzAtIEMA==", "optional": true, "dependencies": { - "@aws-sdk/types": "3.398.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/types": "^2.2.2", + "@aws-sdk/node-config-provider": "3.357.0", + "@aws-sdk/types": "3.357.0", "tslib": "^2.5.0" }, "engines": { @@ -788,6 +1269,19 @@ } } }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.310.0.tgz", + "integrity": "sha512-DnLfFT8uCO22uOJc0pt0DsSNau1GTisngBCDw8jQuWT5CqogMJu4b/uXmwEqfj8B3GX6Xsz8zOd6JpRlPftQoA==", + "optional": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.310.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/util-utf8-browser": { "version": "3.259.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", @@ -809,16 +1303,15 @@ } }, "node_modules/@azure/core-auth": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", - "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz", + "integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", - "@azure/core-util": "^1.1.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=12.0.0" } }, "node_modules/@azure/core-client": { @@ -852,9 +1345,9 @@ } }, "node_modules/@azure/core-lro": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", - "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.3.tgz", + "integrity": "sha512-ubkOf2YCnVtq7KqEJQqAI8dDD5rH1M6OP5kW0KO/JQyTaxLA0N0pjFWvvaysCj9eHMNBcuuoZXhhl0ypjod2DA==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-util": "^1.2.0", @@ -877,9 +1370,9 @@ } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.12.0.tgz", - "integrity": "sha512-+MnSB0vGZjszSzr5AW8z93/9fkDu2RLtWmAN8gskURq7EW2sSwqy8jZa0V26rjuBVkwhdA3Hw8z3VWoeBUOw+A==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.11.0.tgz", + "integrity": "sha512-nB4KXl6qAyJmBVLWA7SakT4tzpYZTCk4pvRBeI+Ye0WYSOrlTqlMhc4MSS/8atD3ufeYWdkN380LLoXlUUzThw==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.4.0", @@ -907,9 +1400,9 @@ } }, "node_modules/@azure/core-util": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.4.0.tgz", - "integrity": "sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz", + "integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", "tslib": "^2.2.0" @@ -964,9 +1457,9 @@ } }, "node_modules/@azure/keyvault-keys": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.2.tgz", - "integrity": "sha512-VdIH6PjbQ3J5ntK+xeI8eOe1WsDxF9ndXw8BPR/9MZVnIj0vQNtNCS6gpR7EFQeGcs8XjzMfHm0AvKGErobqJQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.7.1.tgz", + "integrity": "sha512-zfmlZQCw1Yz+aPhgZmWOYBUzaKmfBzR2yceAE4S6hKDl7YZraTguuXmtFbCqjRvpz+pIMKAK25fENay9mFy1hQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -974,7 +1467,7 @@ "@azure/core-http-compat": "^1.3.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.1", + "@azure/core-rest-pipeline": "^1.8.0", "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.0.0", "@azure/logger": "^1.0.0", @@ -996,20 +1489,20 @@ } }, "node_modules/@azure/msal-browser": { - "version": "2.38.2", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.38.2.tgz", - "integrity": "sha512-71BeIn2we6LIgMplwCSaMq5zAwmalyJR3jFcVOZxNVfQ1saBRwOD+P77nLs5vrRCedVKTq8RMFhIOdpMLNno0A==", + "version": "2.37.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-2.37.1.tgz", + "integrity": "sha512-EoKQISEpIY39Ru1OpWkeFZBcwp6Y0bG81bVmdyy4QJebPPDdVzfm62PSU0XFIRc3bqjZ4PBKBLMYLuo9NZYAow==", "dependencies": { - "@azure/msal-common": "13.3.0" + "@azure/msal-common": "13.1.0" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.0.tgz", - "integrity": "sha512-/VFWTicjcJbrGp3yQP7A24xU95NiDMe23vxIU1U6qdRPFsprMDNUohMudclnd+WSHE4/McqkZs/nUU3sAKkVjg==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.1.0.tgz", + "integrity": "sha512-wj+ULrRB0HTuMmtrMjg8j3guCx32GE2BCPbsMCZkHgL1BZetC3o/Su5UJEQMX1HNc9CrIaQNx5WaKWHygYDe0g==", "engines": { "node": ">=0.8.0" } @@ -1023,11 +1516,11 @@ } }, "node_modules/@azure/msal-node": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.18.2.tgz", - "integrity": "sha512-bLbuhF9Q5cgwj9tt8R7EV9MbCwGuFgZiv6Gw0VvHx5AcWHhh2m8hYginGagB4EucxKueFDwZP6aZVAxfuD/lUQ==", + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-1.17.3.tgz", + "integrity": "sha512-slsa+388bQQWnWH1V91KL+zV57rIp/0OQFfF0EmVMY8gnEIkAnpWWFUVBTTMbxEyjEFMk5ZW9xiHvHBcYFHzDw==", "dependencies": { - "@azure/msal-common": "13.3.0", + "@azure/msal-common": "13.1.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -1036,20 +1529,20 @@ } }, "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.3.0.tgz", - "integrity": "sha512-/VFWTicjcJbrGp3yQP7A24xU95NiDMe23vxIU1U6qdRPFsprMDNUohMudclnd+WSHE4/McqkZs/nUU3sAKkVjg==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-13.1.0.tgz", + "integrity": "sha512-wj+ULrRB0HTuMmtrMjg8j3guCx32GE2BCPbsMCZkHgL1BZetC3o/Su5UJEQMX1HNc9CrIaQNx5WaKWHygYDe0g==", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.22.10", "chalk": "^2.4.2" }, "engines": { @@ -1066,9 +1559,9 @@ } }, "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", @@ -1076,15 +1569,15 @@ "@babel/generator": "^7.22.10", "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", + "json5": "^2.2.2", "semver": "^6.3.1" }, "engines": { @@ -1105,9 +1598,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.11.tgz", - "integrity": "sha512-YjOYZ3j7TjV8OhLW6NCtyg8G04uStATEUe5eiLuCZaXz2VSDQ3dsAtm2D+TuQyAqNMUK2WacGo0/uma9Pein1w==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.10.tgz", + "integrity": "sha512-0J8DNPRXQRLeR9rPaUMM3fA+RbixjnVLe/MRMYCkp3hzgsSuxCHQ8NN8xQG1wIHKJ4a1DTROTvFJdW+B5/eOsg==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -1171,12 +1664,12 @@ } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz", - "integrity": "sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1208,9 +1701,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz", - "integrity": "sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz", + "integrity": "sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -1218,10 +1711,10 @@ "@babel/helper-function-name": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" + "@babel/helper-split-export-declaration": "^7.22.5", + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -1240,14 +1733,14 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", - "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.5.tgz", + "integrity": "sha512-1VpEFOIbMRaXyDeUwUfmTIxExLwQ+zkW+Bh5zXpApA3oQedBx9v/updixWxnx/bZpKw7u8VxWjb/qWpIcmPq8A==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", - "semver": "^6.3.1" + "semver": "^6.3.0" }, "engines": { "node": ">=6.9.0" @@ -1397,20 +1890,20 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", - "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz", + "integrity": "sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-simple-access": { @@ -1491,23 +1984,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", "dev": true, "dependencies": { "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", @@ -1519,9 +2012,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz", - "integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1867,9 +2360,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.11.tgz", - "integrity": "sha512-0pAlmeRJn6wU84zzZsEOx1JV1Jf8fqO9ok7wofIJwUnplYo247dcd24P+cMJht7ts9xkzdtB0EPHmOb7F+KzXw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz", + "integrity": "sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", @@ -1948,12 +2441,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -2050,9 +2543,9 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2082,9 +2575,9 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2130,9 +2623,9 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", - "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2161,9 +2654,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2208,12 +2701,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz", - "integrity": "sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -2225,13 +2718,13 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz", - "integrity": "sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.5" }, @@ -2290,9 +2783,9 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2306,9 +2799,9 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2322,13 +2815,13 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz", - "integrity": "sha512-nX8cPFa6+UmbepISvlf5jhQyaC7ASs/7UxHmMkuJ/k5xSHvDPPaibMo+v3TXwU/Pjqhep/nFNpd3zn4YR59pnw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.10", + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-transform-parameters": "^7.22.5" @@ -2357,9 +2850,9 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", - "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2373,9 +2866,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz", - "integrity": "sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz", + "integrity": "sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2421,13 +2914,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -2747,23 +3240,17 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", + "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.14.0" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/runtime/node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, "node_modules/@babel/template": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", @@ -2779,9 +3266,9 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.10", @@ -2790,8 +3277,8 @@ "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2800,9 +3287,9 @@ } }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", @@ -2852,22 +3339,16 @@ } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.2.0.tgz", - "integrity": "sha512-wErmsWCbsmig8sQKkM6pFhr/oPha1bHfvxsUY5CYSQxwyhA9Ulrs8EqCgClhg4Tgg2XapVstGqSVcz0xOYizZA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", + "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], "engines": { "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" } }, "node_modules/@csstools/media-query-list-parser": { @@ -2916,9 +3397,9 @@ } }, "node_modules/@cypress/request": { - "version": "2.88.12", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.12.tgz", - "integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==", + "version": "2.88.11", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.11.tgz", + "integrity": "sha512-M83/wfQ1EkspjkE2lNWNV5ui2Cv7UCv1swW1DqljahbzLVWltcsexQh8jYtuS/vzFXP+HySntGM83ZXA9fn17w==", "dev": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -2936,7 +3417,7 @@ "performance-now": "^2.1.0", "qs": "~6.10.3", "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" }, @@ -2977,6 +3458,20 @@ "ms": "^2.1.1" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", + "integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==", + "dev": true, + "dependencies": { + "comment-parser": "1.4.0", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -3353,9 +3848,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3457,13 +3952,14 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.9.tgz", - "integrity": "sha512-YJsOehVXzgurc+lLAxYnlSMc1p/Gu6VAvnfx0ATi2nzvr0YZcjhmZDeY8SeAKv1M7zE3aEJH0Xo9mK1iZ8GYoQ==", + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", + "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", "dependencies": { + "@types/long": "^4.0.1", "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.4", + "long": "^4.0.0", + "protobufjs": "^7.0.0", "yargs": "^17.7.2" }, "bin": { @@ -3722,16 +4218,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", + "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2", "slash": "^3.0.0" }, "engines": { @@ -3809,37 +4305,37 @@ } }, "node_modules/@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", + "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/console": "^29.6.2", + "@jest/reporters": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.6.2", + "jest-haste-map": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.6.2", + "jest-resolve-dependencies": "^29.6.2", + "jest-runner": "^29.6.2", + "jest-runtime": "^29.6.2", + "jest-snapshot": "^29.6.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", + "jest-watcher": "^29.6.2", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.6.2", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -3926,88 +4422,88 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", + "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/fake-timers": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", - "jest-mock": "^29.6.3" + "jest-mock": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", + "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", "dev": true, "dependencies": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "expect": "^29.6.2", + "jest-snapshot": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", + "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", "dev": true, "dependencies": { - "jest-get-type": "^29.6.3" + "jest-get-type": "^29.4.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", + "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-message-util": "^29.6.2", + "jest-mock": "^29.6.2", + "jest-util": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", + "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "@jest/environment": "^29.6.2", + "@jest/expect": "^29.6.2", + "@jest/types": "^29.6.1", + "jest-mock": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", + "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/console": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -4016,13 +4512,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-instrument": "^5.1.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2", + "jest-worker": "^29.6.2", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -4111,9 +4607,9 @@ } }, "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", + "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -4123,9 +4619,9 @@ } }, "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", + "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -4137,13 +4633,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", + "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/console": "^29.6.2", + "@jest/types": "^29.6.1", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -4152,14 +4648,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", + "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.6.2", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.6.2", "slash": "^3.0.0" }, "engines": { @@ -4167,22 +4663,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", + "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-haste-map": "^29.6.2", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.6.2", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -4269,12 +4765,12 @@ } }, "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", + "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", + "@jest/schemas": "^29.6.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -4370,9 +4866,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { "node": ">=6.0.0" @@ -4388,9 +4884,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", + "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -4404,15 +4900,21 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@js-joda/core": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.5.3.tgz", @@ -4459,9 +4961,9 @@ } }, "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", @@ -4486,14 +4988,6 @@ "eslint-scope": "5.1.1" } }, - "node_modules/@noble/ciphers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.2.0.tgz", - "integrity": "sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw==", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@noble/curves": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", @@ -4881,9 +5375,9 @@ "dev": true }, "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.4.1.tgz", + "integrity": "sha512-axlrvsHlHlFmKKMEg4VyvMzFr93JWJj4eIfXY1STVuO2fsImCa7ncaiG5gC8HKOX590AW5RtRsC41/B+OfrSqw==", "engines": { "node": ">=14.16" }, @@ -4909,319 +5403,13 @@ "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@smithy/abort-controller": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.5.tgz", - "integrity": "sha512-byVZ2KWLMPYAZGKjRpniAzLcygJO4ruClZKdJTuB0eCB76ONFTdptBHlviHpAZXknRz7skYWPfcgO9v30A1SyA==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.5.tgz", - "integrity": "sha512-n0c2AXz+kjALY2FQr7Zy9zhYigXzboIh1AuUUVCqFBKFtdEvTwnwPXrTDoEehLiRTUHNL+4yzZ3s+D0kKYSLSg==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.5.tgz", - "integrity": "sha512-KFcf/e0meFkQNyteJ65f1G19sgUEY1e5zL7hyAEUPz2SEfBmC9B37WyRq87G3MEEsvmAWwCRu7nFFYUKtR3svQ==", - "optional": true, - "dependencies": { - "@smithy/node-config-provider": "^2.0.5", - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.5.tgz", - "integrity": "sha512-iqR6OuOV3zbQK8uVs9o+9AxhVk8kW9NAxA71nugwUB+kTY9C35pUd0A5/m4PRT0Y0oIW7W4kgnSR3fdYXQjECw==", - "optional": true, - "dependencies": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.5.tgz", - "integrity": "sha512-EzFoMowdBNy1VqtvkiXgPFEdosIAt4/4bgZ8uiDiUyfhmNXq/3bV+CagPFFBsgFOR/X2XK4zFZHRsoa7PNHVVg==", - "optional": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/querystring-builder": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.5.tgz", - "integrity": "sha512-mk551hIywBITT+kXruRNXk7f8Fy7DTzBjZJSr/V6nolYKmUHIG3w5QU6nO9qPYEQGKc/yEPtkpdS28ndeG93lA==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.5.tgz", - "integrity": "sha512-0wEi+JT0hM+UUwrJVYbqjuGFhy5agY/zXyiN7BNAJ1XoCDjU5uaNSj8ekPWsXd/d4yM6NSe8UbPd8cOc1+3oBQ==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.5.tgz", - "integrity": "sha512-E7VwV5H02fgZIUGRli4GevBCAPvkyEI/fgl9SU47nPPi3DAAX3nEtUb8xfGbXjOcJ5BdSUoWWZn42tEd/blOqA==", - "optional": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.5.tgz", - "integrity": "sha512-tyzDuoNTbsMQCq5Xkc4QOt6e2GACUllQIV8SQ5fc59FtOIV9/vbf58/GxVjZm2o8+MMbdDBANjTDZe/ijZKfyA==", - "optional": true, - "dependencies": { - "@smithy/middleware-serde": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.5.tgz", - "integrity": "sha512-ulIfbFyzQTVnJbLjUl1CTSi0etg6tej/ekwaLp0Gn8ybUkDkKYa+uB6CF/m2J5B6meRwyJlsryR+DjaOVyiicg==", - "optional": true, - "dependencies": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.5.tgz", - "integrity": "sha512-in0AA5sous74dOfTGU9rMJBXJ0bDVNxwdXtEt5lh3FVd2sEyjhI+rqpLLRF1E4ixbw3RSEf80hfRpcPdjg4vvQ==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.5.tgz", - "integrity": "sha512-LRtjV9WkhONe2lVy+ipB/l1GX60ybzBmFyeRUoLUXWKdnZ3o81jsnbKzMK8hKq8eFSWPk+Lmyx6ZzCQabGeLxg==", - "optional": true, - "dependencies": { - "@smithy/property-provider": "^2.0.5", - "@smithy/shared-ini-file-loader": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.5.tgz", - "integrity": "sha512-lZm5DZf4b3V0saUw9WTC4/du887P6cy2fUyQgQQKRRV6OseButyD5yTzeMmXE53CaXJBMBsUvvIQ0hRVxIq56w==", - "optional": true, - "dependencies": { - "@smithy/abort-controller": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/querystring-builder": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.5.tgz", - "integrity": "sha512-cAFSUhX6aiHcmpWfrCLKvwBtgN1F6A0N8qY/8yeSi0LRLmhGqsY1/YTxFE185MCVzYbqBGXVr9TBv4RUcIV4rA==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@smithy/protocol-http": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.5.tgz", - "integrity": "sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", + "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", "optional": true, "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.5.tgz", - "integrity": "sha512-4DCX9krxLzATj+HdFPC3i8pb7XTAWzzKqSw8aTZMjXjtQY+vhe4azMAqIvbb6g7JKwIkmkRAjK6EXO3YWSnJVQ==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.5.tgz", - "integrity": "sha512-C2stCULH0r54KBksv3AWcN8CLS3u9+WsEW8nBrvctrJ5rQTNa1waHkffpVaiKvcW2nP0aIMBPCobD/kYf/q9mA==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "optional": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.5.tgz", - "integrity": "sha512-Mvtk6FwMtfbKRC4YuSsIqRYp9WTxsSUJVVo2djgyhcacKGMqicHDWSAmgy3sDrKv+G/G6xTZCPwm6pJARtdxVg==", - "optional": true, - "dependencies": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.5.tgz", - "integrity": "sha512-ABIzXmUDXK4n2c9cXjQLELgH2RdtABpYKT+U131e2I6RbCypFZmxIHmIBufJzU2kdMCQ3+thBGDWorAITFW04A==", - "optional": true, - "dependencies": { - "@smithy/eventstream-codec": "^2.0.5", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.5.tgz", - "integrity": "sha512-kCTFr8wfOAWKDzGvfBElc6shHigWtHNhMQ1IbosjC4jOlayFyZMSs2PysKB+Ox/dhQ41KqOzgVjgiQ+PyWqHMQ==", - "optional": true, - "dependencies": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-stream": "^2.0.5", + "@smithy/types": "^1.1.0", "tslib": "^2.5.0" }, "engines": { @@ -5229,9 +5417,9 @@ } }, "node_modules/@smithy/types": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.2.tgz", - "integrity": "sha512-4PS0y1VxDnELGHGgBWlDksB2LJK8TG8lcvlWxIsgR+8vROI7Ms8h1P4FQUx+ftAX2QZv5g1CJCdhdRmQKyonyw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", + "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", "optional": true, "dependencies": { "tslib": "^2.5.0" @@ -5240,189 +5428,6 @@ "node": ">=14.0.0" } }, - "node_modules/@smithy/url-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.5.tgz", - "integrity": "sha512-OdMBvZhpckQSkugCXNJQCvqJ71wE7Ftxce92UOQLQ9pwF6hoS5PLL7wEfpnuEXtStzBqJYkzu1C1ZfjuFGOXAA==", - "optional": true, - "dependencies": { - "@smithy/querystring-parser": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "optional": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "optional": true, - "dependencies": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.5.tgz", - "integrity": "sha512-yciP6TPttLsj731aHTvekgyuCGXQrEAJibEwEWAh3kzaDsfGAVCuZSBlyvC2Dl3TZmHKCOQwHV8mIE7KQCTPuQ==", - "optional": true, - "dependencies": { - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.5.tgz", - "integrity": "sha512-M07t99rWasXt+IaDZDyP3BkcoEm/mgIE1RIMASrE49LKSNxaVN7PVcgGc77+4uu2kzBAyqJKy79pgtezuknyjQ==", - "optional": true, - "dependencies": { - "@smithy/config-resolver": "^2.0.5", - "@smithy/credential-provider-imds": "^2.0.5", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "optional": true, - "dependencies": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.5.tgz", - "integrity": "sha512-ylx27GwI05xLpYQ4hDIfS15vm+wYjNN0Sc2P0FxuzgRe8v0BOLHppGIQ+Bezcynk8C9nUzsUue3TmtRhjut43g==", - "optional": true, - "dependencies": { - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "optional": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "optional": true, - "dependencies": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", @@ -5576,9 +5581,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.36", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.36.tgz", - "integrity": "sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==", + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -5640,9 +5645,9 @@ "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" }, "node_modules/@types/koa": { - "version": "2.13.8", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.8.tgz", - "integrity": "sha512-Ugmxmgk/yPRW3ptBTh9VjOLwsKWJuGbymo1uGX0qdaqqL18uJiiG1ZoV0rxCOYSaDGhvEp5Ece02Amx0iwaxQQ==", + "version": "2.13.6", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.6.tgz", + "integrity": "sha512-diYUfp/GqfWBAiwxHtYJ/FQYIXhlEhlyaU7lB/bWQrx4Il9lCET5UwpFy3StOAohfsxxvEQ11qIJgT1j2tfBvw==", "dependencies": { "@types/accepts": "*", "@types/content-disposition": "*", @@ -5662,6 +5667,11 @@ "@types/koa": "*" } }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -5674,9 +5684,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", - "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==" + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", + "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -5735,9 +5745,9 @@ } }, "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.17.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.12.tgz", - "integrity": "sha512-d6xjC9fJ/nSnfDeU0AMDsaJyb1iHsqCSOdi84w4u+SlN/UgQdY5tRhpMzaFYsI4mnpvgTivEaQd0yOUhAtOnEQ==", + "version": "18.17.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.4.tgz", + "integrity": "sha512-ATL4WLgr7/W40+Sp1WnNTSKbgVn6Pvhc/2RHAdt8fl6NsQyp4oPCi2eKcGOvA494bwf1K/W6nGgZ9TwDqvpjdw==", "dev": true }, "node_modules/@types/stack-utils": { @@ -5811,9 +5821,9 @@ } }, "node_modules/@vitejs/plugin-legacy/node_modules/core-js": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.1.tgz", - "integrity": "sha512-lqufgNn9NLnESg5mQeYsxQP5w7wrViSj0jr/kv6ECQiByzQkrn1MKvV0L3acttpDqfQrHLwr2KCMgX5b8X+lyQ==", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz", + "integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==", "dev": true, "hasInstallScript": true, "funding": { @@ -5972,6 +5982,18 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -5985,9 +6007,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -6079,17 +6101,28 @@ } }, "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", "optional": true, "dependencies": { + "debug": "^4.1.0", + "depd": "^2.0.0", "humanize-ms": "^1.2.1" }, "engines": { "node": ">= 8.0.0" } }, + "node_modules/agentkeepalive/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "optional": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -6222,6 +6255,15 @@ } ] }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", @@ -6271,25 +6313,6 @@ "node": ">=8" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -6404,15 +6427,15 @@ } }, "node_modules/babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", + "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.4", + "@jest/transform": "^29.6.2", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", + "babel-preset-jest": "^29.5.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -6516,35 +6539,10 @@ "node": ">=8" } }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -6628,12 +6626,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", + "babel-plugin-jest-hoist": "^29.5.0", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -6696,11 +6694,6 @@ "readable-stream": "~1.0.2" } }, - "node_modules/barse/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, "node_modules/barse/node_modules/readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -6925,9 +6918,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -6944,9 +6937,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", "update-browserslist-db": "^1.0.11" }, "bin": { @@ -7036,6 +7029,18 @@ "node": ">=10.0.0" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bulk-write-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/bulk-write-stream/-/bulk-write-stream-2.0.1.tgz", @@ -7110,14 +7115,14 @@ } }, "node_modules/cacheable-request": { - "version": "10.2.13", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz", - "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==", + "version": "10.2.12", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.12.tgz", + "integrity": "sha512-qtWGB5kn2OLjx47pYUkWicyOpK1vy9XZhq8yRTXOy+KAmjjESSRLx6SiExnnaGGUP1NM6/vmygMu0fGylNh9tw==", "dependencies": { "@types/http-cache-semantics": "^4.0.1", "get-stream": "^6.0.1", "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", + "keyv": "^4.5.2", "mimic-response": "^4.0.0", "normalize-url": "^8.0.0", "responselike": "^3.0.0" @@ -7138,9 +7143,9 @@ } }, "node_modules/cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", "dev": true, "engines": { "node": ">=6" @@ -7219,9 +7224,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001524", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", - "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", + "version": "1.0.30001507", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001507.tgz", + "integrity": "sha512-SFpUDoSLCaE5XYL2jfqe9ova/pbQHEmbheDf5r4diNwbAgR3qxM9NQtfsiSscjqoya5K7kFcHPUQ+VsUkIJR4A==", "dev": true, "funding": [ { @@ -7577,6 +7582,15 @@ "node": ">= 6" } }, + "node_modules/comment-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", + "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commist": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", @@ -7877,12 +7891,12 @@ } }, "node_modules/core-js-compat": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.1.tgz", - "integrity": "sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.0.tgz", + "integrity": "sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw==", "dev": true, "dependencies": { - "browserslist": "^4.21.10" + "browserslist": "^4.21.9" }, "funding": { "type": "opencollective", @@ -7925,9 +7939,9 @@ } }, "node_modules/cpu-features": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.9.tgz", - "integrity": "sha512-AKjgn2rP2yJyfbepsmLfiYcmtNn/2eUvocUyM/09yB0YDiz39HteK/5/T4Onf0pmdYDMgkBoGvRLvEguzyL7wQ==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.8.tgz", + "integrity": "sha512-BbHBvtYhUhksqTjr6bhNOjGgMnhwhGTQmOoZGD+K7BCaQDCuZl/Ve1ZxUSMRwVC4D/rkCPQ2MAIeYzrWyK7eEg==", "dev": true, "hasInstallScript": true, "optional": true, @@ -7940,9 +7954,9 @@ } }, "node_modules/croner": { - "version": "6.0.7", - "resolved": "https://registry.npmjs.org/croner/-/croner-6.0.7.tgz", - "integrity": "sha512-k3Xx3Rcclfr60Yx4TmvsF3Yscuiql8LSvYLaphTsaq5Hk8La4Z/udmUANMOTKpgGGroI2F6/XOr9cU9OFkYluQ==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/croner/-/croner-6.0.5.tgz", + "integrity": "sha512-rVUsq9ScPjIN9AUARCRyF50KAc5x0UvskctcAG08KJ+5H3pDEyPIVf9tGe3suQMXI3g+bd/L+7kkSNUUyigzzg==", "engines": { "node": ">=6.0" } @@ -8079,13 +8093,13 @@ "dev": true }, "node_modules/cypress": { - "version": "12.17.4", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.4.tgz", - "integrity": "sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==", + "version": "12.17.3", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.3.tgz", + "integrity": "sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@cypress/request": "2.88.12", + "@cypress/request": "^2.88.11", "@cypress/xvfb": "^1.2.4", "@types/node": "^16.18.39", "@types/sinonjs__fake-timers": "8.1.1", @@ -8120,7 +8134,6 @@ "minimist": "^1.2.8", "ospath": "^1.2.2", "pretty-bytes": "^5.6.0", - "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", "semver": "^7.5.3", @@ -8137,9 +8150,9 @@ } }, "node_modules/cypress/node_modules/@types/node": { - "version": "16.18.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.46.tgz", - "integrity": "sha512-Mnq3O9Xz52exs3mlxMcQuA7/9VFe/dXcrgAyfjLkABIqxXKOgBRjyazTxUbjsxDa4BP7hhPliyjVTP9RDP14xg==", + "version": "16.18.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.40.tgz", + "integrity": "sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==", "dev": true }, "node_modules/cypress/node_modules/ansi-styles": { @@ -8256,9 +8269,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", - "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==" + "version": "1.11.8", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", + "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -8277,15 +8290,12 @@ } }, "node_modules/decamelize": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", - "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/decamelize-keys": { @@ -8304,15 +8314,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/decamelize-keys/node_modules/map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", @@ -8460,9 +8461,9 @@ "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==" }, "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", "engines": { "node": ">=8" } @@ -8482,9 +8483,9 @@ "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==" }, "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -8565,9 +8566,9 @@ } }, "node_modules/dompurify": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.7.tgz", - "integrity": "sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.5.tgz", + "integrity": "sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==", "dev": true }, "node_modules/domutils": { @@ -8631,9 +8632,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.504", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.504.tgz", - "integrity": "sha512-cSMwIAd8yUh54VwitVRVvHK66QqHWE39C3DRj8SWiXitEpVSY3wNPD9y1pxQtLIi4w3UdzF9klLsmuPshz09DQ==", + "version": "1.4.440", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz", + "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw==", "dev": true }, "node_modules/emittery": { @@ -8765,13 +8766,12 @@ } }, "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" + "ansi-colors": "^4.1.1" }, "engines": { "node": ">=8.6" @@ -8813,18 +8813,17 @@ } }, "node_modules/es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", "dependencies": { "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.0", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -8844,18 +8843,14 @@ "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", + "regexp.prototype.flags": "^1.4.3", "safe-regex-test": "^1.0.0", "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" + "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -8865,15 +8860,15 @@ } }, "node_modules/es-aggregate-error": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.10.tgz", - "integrity": "sha512-qX3K9eP7HcgeFckzRy1T5Mtb7wYwZt9ahFteBDigG5Te0vGOmOH3dHDncBiuNkZBX9i+C8LgSbpqSEl97gN11Q==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.9.tgz", + "integrity": "sha512-fvnX40sb538wdU6r4s35cq4EY6Lr09Upj40BEVem4LEsuW8XgQep9yD5Q1U2KftokNp1rWODFJ2qwZSsAjFpbg==", "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "function-bind": "^1.1.1", "functions-have-names": "^1.2.3", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.1.3", "globalthis": "^1.0.3", "has-property-descriptors": "^1.0.0" }, @@ -9030,6 +9025,41 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "46.4.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz", + "integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.40.1", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.0", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.5.4", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-plugin-vue": { "version": "8.7.1", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz", @@ -9152,9 +9182,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -9168,9 +9198,9 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -9189,9 +9219,9 @@ } }, "node_modules/eslint/node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -9245,12 +9275,12 @@ } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -9262,9 +9292,9 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -9360,6 +9390,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/event-to-promise": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.7.0.tgz", @@ -9425,16 +9464,17 @@ } }, "node_modules/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", + "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.4", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" + "@jest/expect-utils": "^29.6.2", + "@types/node": "*", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -9795,17 +9835,16 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", "dev": true, "dependencies": { - "flatted": "^3.2.7", - "keyv": "^4.5.3", + "flatted": "^3.1.0", "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { @@ -9857,9 +9896,9 @@ } }, "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", "engines": { "node": ">=14" }, @@ -9985,9 +10024,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, @@ -10004,14 +10043,14 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -10035,14 +10074,14 @@ } }, "node_modules/gamedig": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-4.0.7.tgz", - "integrity": "sha512-A8bJ23ulAEp8A4ZJAHp5cMkWu4ymf6AQdOPBAa2asHQqAnf2/bIa07ClcQeeCp+bQWYqJAcW7xvUqjruSrCX4A==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-4.0.6.tgz", + "integrity": "sha512-h0k9n/e5vNrd9Mh2wyFUp2Vo7ABWbDkdBxKC6FNJLOZiU5d9Z29bntGeYbXtOkcRWoV6Q63wSAJ3jLWxYQkpZw==", "dependencies": { "cheerio": "^1.0.0-rc.10", "compressjs": "^1.0.2", "gbxremote": "^0.2.1", - "got": "^12.1.0", + "got": "^12.0.3", "iconv-lite": "^0.6.3", "long": "^5.2.0", "minimist": "^1.2.6", @@ -10056,6 +10095,11 @@ "node": ">=14.0.0" } }, + "node_modules/gamedig/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", @@ -10903,6 +10947,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -10927,9 +10986,9 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dependencies": { "has": "^1.0.3" }, @@ -11158,11 +11217,15 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", "dependencies": { - "which-typed-array": "^1.1.11" + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -11212,9 +11275,9 @@ } }, "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" }, "node_modules/isemail": { "version": "3.2.0", @@ -11256,19 +11319,28 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "semver": "^6.3.0" }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, "node_modules/istanbul-lib-report": { @@ -11349,9 +11421,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.0.tgz", - "integrity": "sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -11366,15 +11438,15 @@ } }, "node_modules/jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", + "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/core": "^29.6.2", + "@jest/types": "^29.6.1", "import-local": "^3.0.2", - "jest-cli": "^29.6.4" + "jest-cli": "^29.6.2" }, "bin": { "jest": "bin/jest.js" @@ -11392,13 +11464,12 @@ } }, "node_modules/jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", "dev": true, "dependencies": { "execa": "^5.0.0", - "jest-util": "^29.6.3", "p-limit": "^3.1.0" }, "engines": { @@ -11449,29 +11520,44 @@ "node": ">=10.17.0" } }, - "node_modules/jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "node_modules/jest-changed-files/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/types": "^29.6.3", + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus": { + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", + "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.2", + "@jest/expect": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-each": "^29.6.2", + "jest-matcher-utils": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-runtime": "^29.6.2", + "jest-snapshot": "^29.6.2", + "jest-util": "^29.6.2", "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.6.2", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -11538,6 +11624,21 @@ "node": ">=8" } }, + "node_modules/jest-circus/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jest-circus/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11551,21 +11652,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", + "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/core": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/types": "^29.6.1", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-config": "^29.6.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -11655,31 +11756,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", + "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", - "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", + "@jest/test-sequencer": "^29.6.2", + "@jest/types": "^29.6.1", + "babel-jest": "^29.6.2", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-circus": "^29.6.2", + "jest-environment-node": "^29.6.2", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.6.2", + "jest-runner": "^29.6.2", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.6.2", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -11770,15 +11871,15 @@ } }, "node_modules/jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", + "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11855,9 +11956,9 @@ } }, "node_modules/jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -11867,16 +11968,16 @@ } }, "node_modules/jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", + "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-get-type": "^29.4.3", + "jest-util": "^29.6.2", + "pretty-format": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -11953,46 +12054,46 @@ } }, "node_modules/jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", + "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/environment": "^29.6.2", + "@jest/fake-timers": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-mock": "^29.6.2", + "jest-util": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", + "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.6.2", + "jest-worker": "^29.6.2", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -12004,28 +12105,28 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", + "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", "dev": true, "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-get-type": "^29.4.3", + "pretty-format": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", + "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.4", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-diff": "^29.6.2", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12102,18 +12203,18 @@ } }, "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", + "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.6.2", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -12192,14 +12293,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", + "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@types/node": "*", - "jest-util": "^29.6.3" + "jest-util": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12223,26 +12324,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", + "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.6.2", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-util": "^29.6.2", + "jest-validate": "^29.6.2", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -12252,13 +12353,13 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", + "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", "dev": true, "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12335,30 +12436,30 @@ } }, "node_modules/jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", + "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/console": "^29.6.2", + "@jest/environment": "^29.6.2", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.6.2", + "jest-haste-map": "^29.6.2", + "jest-leak-detector": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-resolve": "^29.6.2", + "jest-runtime": "^29.6.2", + "jest-util": "^29.6.2", + "jest-watcher": "^29.6.2", + "jest-worker": "^29.6.2", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -12424,6 +12525,31 @@ "node": ">=8" } }, + "node_modules/jest-runner/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/jest-runner/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12437,31 +12563,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", + "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/environment": "^29.6.2", + "@jest/fake-timers": "^29.6.2", + "@jest/globals": "^29.6.2", + "@jest/source-map": "^29.6.0", + "@jest/test-result": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-haste-map": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-mock": "^29.6.2", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.6.2", + "jest-snapshot": "^29.6.2", + "jest-util": "^29.6.2", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -12540,9 +12666,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", + "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -12550,20 +12676,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/expect-utils": "^29.6.2", + "@jest/transform": "^29.6.2", + "@jest/types": "^29.6.1", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.4", + "expect": "^29.6.2", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-diff": "^29.6.2", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.6.2", + "jest-message-util": "^29.6.2", + "jest-util": "^29.6.2", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.6.2", "semver": "^7.5.3" }, "engines": { @@ -12641,12 +12767,12 @@ } }, "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", + "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -12728,17 +12854,17 @@ } }, "node_modules/jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", + "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "^29.6.1", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", + "jest-get-type": "^29.4.3", "leven": "^3.1.0", - "pretty-format": "^29.6.3" + "pretty-format": "^29.6.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -12827,18 +12953,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", + "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", - "@jest/types": "^29.6.3", + "@jest/test-result": "^29.6.2", + "@jest/types": "^29.6.1", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.3", + "jest-util": "^29.6.2", "string-length": "^4.0.1" }, "engines": { @@ -12916,13 +13042,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", + "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.3", + "jest-util": "^29.6.2", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -13016,6 +13142,15 @@ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsesc": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", @@ -13095,9 +13230,9 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", "dependencies": { "jws": "^3.2.2", "lodash": "^4.17.21", @@ -13162,9 +13297,9 @@ } }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", "dependencies": { "json-buffer": "3.0.1" } @@ -13259,9 +13394,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.28.0.tgz", - "integrity": "sha512-9pSL5XB4J+ifHP0e0jmmC98OGC1nL8/JjS+fi6mnTlIf//yt/MfVLtKg7S6nCtj/8KTcWX7nRlY0XywoYY1ISQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", + "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", "dev": true }, "node_modules/lazy-ass": { @@ -13310,9 +13445,9 @@ "dev": true }, "node_modules/liquidjs": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.9.2.tgz", - "integrity": "sha512-ygPCgYyiFKQNyRi/CK3s3U5RimosBtrIq7TaMYK5ek93mUl9CZ6xxqw2T+1G4kVc9dAmwI71bWLQNKRToV8SsQ==", + "version": "10.8.4", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.8.4.tgz", + "integrity": "sha512-HSpYAFBVWxhwWsTKPBJgPm3bnwwIzAZjy17XhX7uJCKJ8H6A1YstZSFmPqMmWfSuJOg43RSx+qWVSA1Fu3+B2w==", "dependencies": { "commander": "^10.0.0" }, @@ -13583,9 +13718,9 @@ } }, "node_modules/long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" }, "node_modules/lowercase-keys": { "version": "3.0.0", @@ -13608,9 +13743,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", - "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", + "version": "0.30.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", + "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -13798,6 +13933,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/meow/node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/meow/node_modules/type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", @@ -14095,6 +14242,14 @@ "node": ">=12" } }, + "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", @@ -14259,11 +14414,6 @@ "node": ">= 8.0" } }, - "node_modules/mysql2/node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, "node_modules/mysql2/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -14361,9 +14511,9 @@ } }, "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -14491,9 +14641,9 @@ "integrity": "sha512-i3Sf6khnenl0aXumo0whAlfPWTaBqHxEnVBBxpu3dZ7q69NkPPv71rvPjlDZ5wkeKCTNNUTECljerS5kcYQxRw==" }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/node-ssh": { @@ -14572,11 +14722,10 @@ } }, "node_modules/nostr-tools": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.14.2.tgz", - "integrity": "sha512-QEe8+tMDW0632eNDcQ+EG1edmsCXLV4WPiWLDcdT3uoE+GM15pVcy18sKwbN7SmgO4GKFEqQ49k45eANC6++SQ==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-1.14.0.tgz", + "integrity": "sha512-hwq2i1z5/DneXRE5Zu/TzQuKzVLcB+gOdfT9CeoiScvNw/2dWRGJvyTXIdF92d7NQ7nMcEwqVJPDytLpEpiiKw==", "dependencies": { - "@noble/ciphers": "^0.2.0", "@noble/curves": "1.1.0", "@noble/hashes": "1.3.1", "@scure/base": "1.1.1", @@ -14781,17 +14930,17 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" }, "engines": { "node": ">= 0.8.0" @@ -14812,15 +14961,15 @@ } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -14838,21 +14987,6 @@ "node": ">=8" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -14985,12 +15119,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", + "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^9.1.1", + "minipass": "^5.0.0 || ^6.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -15000,17 +15134,17 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", + "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", "engines": { "node": "14 || >=16.14" } }, "node_modules/path-scurry/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -15080,9 +15214,9 @@ } }, "node_modules/pg-pool": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", - "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.0.tgz", + "integrity": "sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ==", "peerDependencies": { "pg": ">=8.0" } @@ -15200,9 +15334,9 @@ } }, "node_modules/postcss": { - "version": "8.4.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz", - "integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==", + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", "dev": true, "funding": [ { @@ -15286,9 +15420,9 @@ } }, "node_modules/postcss-scss": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.7.tgz", - "integrity": "sha512-xPv2GseoyXPa58Nro7M73ZntttusuCmZdeOojUFR5PZDz2BR62vfYx1w9TyOnp1+nYFowgOMipsCBhxzVkAEPw==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", "dev": true, "funding": [ { @@ -15298,10 +15432,6 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss-scss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" } ], "engines": { @@ -15387,12 +15517,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", + "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", + "@jest/schemas": "^29.6.0", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -15503,9 +15633,9 @@ } }, "node_modules/protobufjs": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", - "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", + "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -15525,6 +15655,11 @@ "node": ">=12.0.0" } }, + "node_modules/protobufjs/node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -15653,15 +15788,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/qrcode/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/qrcode/node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -15731,12 +15857,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -15889,6 +16009,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/read-pkg-up/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -15965,9 +16100,9 @@ } }, "node_modules/redbean-node": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/redbean-node/-/redbean-node-0.3.1.tgz", - "integrity": "sha512-rz71vF7UtJQ14ttZ9I0QuaJ9TOwBCnZb+qHUBiU05f2fLaiQC79liisL3xgkHI8uE9et6HAkG8Z8VPkZbhgxKw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/redbean-node/-/redbean-node-0.3.0.tgz", + "integrity": "sha512-DIkAjMVw8lW2j7GsWivsm9ZKUUKvlTeOmB9lZi8e3VIWu4r6c5ZAeSzoQ4Cytj62SaBWFdCGLPhwdf4/Gql+mw==", "dependencies": { "@types/node": "~20.3.1", "await-lock": "~2.2.2", @@ -15977,11 +16112,6 @@ "lodash": "~4.17.21" } }, - "node_modules/redbean-node/node_modules/@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" - }, "node_modules/redbean-node/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -15991,15 +16121,15 @@ } }, "node_modules/redbean-node/node_modules/glob": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", - "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.0.tgz", + "integrity": "sha512-AQ1/SB9HH0yCx1jXAT4vmCbTOPe5RQ+kCurjbel5xSCGhebumUv+GJZfa1rEqor3XIViqwSEmlkZCQD43RWrBg==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.0.3", "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" }, "bin": { "glob": "dist/cjs/src/bin.js" @@ -16012,9 +16142,9 @@ } }, "node_modules/redbean-node/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", + "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -16026,9 +16156,9 @@ } }, "node_modules/redbean-node/node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-6.0.2.tgz", + "integrity": "sha512-MzWSV5nYVT7mVyWCwn2o7JH13w2TBRmmSqSRCKzTw+lmft9X4z+3wjvs06Tzijo5z4W/kahUCDpRXTF+ZrmF/w==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -16210,18 +16340,12 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -16348,9 +16472,9 @@ } }, "node_modules/rollup": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.1.tgz", - "integrity": "sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==", + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz", + "integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -16444,6 +16568,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/rtlcss/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/rtlcss/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -16491,23 +16630,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -16928,9 +17050,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -17206,6 +17328,23 @@ "node": ">=8" } }, + "node_modules/string.prototype.replaceall": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.replaceall/-/string.prototype.replaceall-1.0.7.tgz", + "integrity": "sha512-xB2WV2GlSCSJT5dMGdhdH1noMPiAB91guiepwTYyWY9/0Vq/TZ7RPmnOSUGAEvry08QIK7EMr28aAii+9jC6kw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", @@ -17329,14 +17468,14 @@ "dev": true }, "node_modules/stylelint": { - "version": "15.10.3", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.3.tgz", - "integrity": "sha512-aBQMMxYvFzJJwkmg+BUUg3YfPyeuCuKo2f+LOw7yYbU8AZMblibwzp9OV4srHVeQldxvSFdz0/Xu8blq2AesiA==", + "version": "15.10.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.10.2.tgz", + "integrity": "sha512-UxqSb3hB74g4DTO45QhUHkJMjKKU//lNUAOWyvPBVPZbCknJ5HjOWWZo+UDuhHa9FLeVdHBZXxu43eXkjyIPWg==", "dev": true, "dependencies": { - "@csstools/css-parser-algorithms": "^2.3.1", - "@csstools/css-tokenizer": "^2.2.0", - "@csstools/media-query-list-parser": "^2.1.4", + "@csstools/css-parser-algorithms": "^2.3.0", + "@csstools/css-tokenizer": "^2.1.1", + "@csstools/media-query-list-parser": "^2.1.2", "@csstools/selector-specificity": "^3.0.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", @@ -17344,7 +17483,7 @@ "css-functions-list": "^3.2.0", "css-tree": "^2.3.1", "debug": "^4.3.4", - "fast-glob": "^3.3.1", + "fast-glob": "^3.3.0", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", "global-modules": "^2.0.0", @@ -17355,13 +17494,13 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.28.0", + "known-css-properties": "^0.27.0", "mathml-tag-names": "^2.1.3", "meow": "^10.1.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.27", + "postcss": "^8.4.25", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", "postcss-selector-parser": "^6.0.13", @@ -17413,6 +17552,15 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, + "node_modules/stylelint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/stylelint/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -17423,9 +17571,9 @@ } }, "node_modules/stylelint/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", "dev": true, "engines": { "node": ">=14" @@ -17434,6 +17582,31 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/stylelint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + } + }, "node_modules/stylelint/node_modules/write-file-atomic": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", @@ -17459,40 +17632,6 @@ "node": ">=4" } }, - "node_modules/supports-hyperlinks": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", - "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - } - }, - "node_modules/supports-hyperlinks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -17511,9 +17650,9 @@ "dev": true }, "node_modules/systemjs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-6.14.2.tgz", - "integrity": "sha512-1TlOwvKWdXxAY9vba+huLu99zrQURDWA8pUTYsRIYDZYQbGyK+pyEP4h4dlySsqo7ozyJBmYD20F+iUHhAltEg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-6.14.1.tgz", + "integrity": "sha512-8ftwWd+XnQtZ/aGbatrN4QFNGrKJzmbtixW+ODpci7pyoTajg4sonPP8aFLESAcuVxaC1FyDESt+SpfFCH9rZQ==", "dev": true }, "node_modules/table": { @@ -17734,14 +17873,21 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/test": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/test/-/test-3.3.0.tgz", + "integrity": "sha512-JKlEohxDIJRjwBH/+BrTcAPHljBALrAHw3Zs99RqZlaC605f6BggqXhxkdqZThbSHgaYPwpNJlf9bTSWkb/1rA==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "minimist": "^1.2.6", + "readable-stream": "^4.3.0", + "string.prototype.replaceall": "^1.0.6" + }, + "bin": { + "node--test": "bin/node--test.js", + "node--test-name-pattern": "bin/node--test-name-pattern.js", + "node--test-only": "bin/node--test-only.js", + "test": "bin/node-core-test.js" } }, "node_modules/test-exclude": { @@ -17758,6 +17904,46 @@ "node": ">=8" } }, + "node_modules/test/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/test/node_modules/readable-stream": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.2.tgz", + "integrity": "sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -17855,27 +18041,16 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "dev": true, "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "psl": "^1.1.28", + "punycode": "^2.1.1" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" + "node": ">=0.8" } }, "node_modules/tr46": { @@ -17912,9 +18087,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, "node_modules/tunnel": { "version": "0.0.6", @@ -17988,54 +18163,6 @@ "node": ">= 0.6" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -18215,16 +18342,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -18264,9 +18381,9 @@ } }, "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, "node_modules/v8-to-istanbul": { @@ -18589,9 +18706,9 @@ } }, "node_modules/vue-eslint-parser/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -18605,9 +18722,9 @@ } }, "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -18759,9 +18876,9 @@ } }, "node_modules/wait-on/node_modules/joi": { - "version": "17.10.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.0.tgz", - "integrity": "sha512-hrazgRSlhzacZ69LdcKfhi3Vu13z2yFfoAzmEov3yFIJlatTdVGUW6vle1zjH8qkzdCn/qGw8rapjqsObbYXAg==", + "version": "17.9.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz", + "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==", "dev": true, "dependencies": { "@hapi/hoek": "^9.0.0", @@ -18780,14 +18897,6 @@ "makeerror": "1.0.12" } }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "engines": { - "node": ">=12" - } - }, "node_modules/whatwg-url": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", @@ -18801,6 +18910,15 @@ "node": ">=14" } }, + "node_modules/whatwg-url/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -18837,15 +18955,16 @@ "dev": true }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -18862,6 +18981,15 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index dd4c6762b..9c548a51f 100644 --- a/package.json +++ b/package.json @@ -24,20 +24,22 @@ "start-server": "node server/server.js", "start-server-dev": "cross-env NODE_ENV=development node server/server.js", "build": "vite build --config ./config/vite.config.js", - "test": "node test/prepare-test-server.js && npm run jest-backend", + "test": "node test/prepare-test-server.js && npm run test-backend", "test-with-build": "npm run build && npm test", + "test-backend": "node test/backend-test-entry.js && npm run jest-backend", + "test-backend:14": "cross-env TEST_BACKEND=1 NODE_OPTIONS=\"--experimental-abortcontroller --no-warnings\" node--test test/backend-test", + "test-backend:18": "cross-env TEST_BACKEND=1 node --test test/backend-test", "jest-backend": "cross-env TEST_BACKEND=1 jest --runInBand --detectOpenHandles --forceExit --config=./config/jest-backend.config.js", "tsc": "tsc", "vite-preview-dist": "vite preview --host --config ./config/vite.config.js", - "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine", - "build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push", - "build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push", + "build-docker": "npm run build && npm run build-docker-full && npm run build-docker-slim", + "build-docker-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2 --target base2 . --push", + "build-docker-base-slim": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base2-slim --target base2-slim . --push", "build-docker-builder-go": "docker buildx build -f docker/builder-go.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:builder-go . --push", - "build-docker-alpine": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:$VERSION-alpine --target release . --push", - "build-docker-debian": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:$VERSION-debian --target release . --push", - "build-docker-nightly": "node ./extra/test-docker.js && npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push", - "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", - "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", + "build-docker-slim": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2-slim -t louislam/uptime-kuma:$VERSION-slim --target release --build-arg BASE_IMAGE=louislam/uptime-kuma:base2-slim . --push", + "build-docker-full": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2 -t louislam/uptime-kuma:$VERSION --target release . --push", + "build-docker-nightly": "node ./extra/test-docker.js && npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly2 --target nightly . --push", + "build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", "setup": "git checkout 1.23.1 && npm ci --production && npm run download-dist", @@ -48,7 +50,6 @@ "compile-install-script": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./extra/compile-install-script.ps1", "test-install-script-rockylinux": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/rockylinux.dockerfile .", "test-install-script-centos7": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/centos7.dockerfile .", - "test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .", "test-install-script-debian": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/debian.dockerfile .", "test-install-script-debian-buster": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/debian-buster.dockerfile .", "test-install-script-ubuntu": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu.dockerfile .", @@ -57,7 +58,6 @@ "simple-dns-server": "node extra/simple-dns-server.js", "simple-mqtt-server": "node extra/simple-mqtt-server.js", "update-language-files": "cd extra/update-language-files && node index.js && cross-env-shell eslint ../../src/languages/$npm_config_language.js --fix", - "ncu-patch": "npm-check-updates -u -t patch", "release-final": "node ./extra/test-docker.js && node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js", "release-beta": "node ./extra/test-docker.js && node extra/beta/update-version.js && npm run build && node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:beta . --target release --push && node ./extra/press-any-key.js && npm run upload-artifacts", "git-remove-tag": "git tag -d", @@ -69,7 +69,9 @@ "cypress-open": "concurrently -k -r \"node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/\" \"cypress open --config-file ./config/cypress.config.js\"", "build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go", "deploy-demo-server": "node extra/deploy-demo-server.js", - "sort-contributors": "node extra/sort-contributors.js" + "sort-contributors": "node extra/sort-contributors.js", + "quick-run-nightly": "docker run --rm --env NODE_ENV=development -p 3001:3001 louislam/uptime-kuma:nightly2", + "start-dev-container": "cd docker && docker-compose -f docker-compose-dev.yml up --force-recreate" }, "dependencies": { "@grpc/grpc-js": "~1.7.3", @@ -106,6 +108,7 @@ "jsonwebtoken": "~9.0.0", "jwt-decode": "~3.1.2", "kafkajs": "^2.2.4", + "knex": "^2.4.2", "limiter": "~2.1.0", "liquidjs": "^10.7.0", "mongodb": "~4.14.0", @@ -165,6 +168,7 @@ "dns2": "~2.0.1", "dompurify": "~2.4.3", "eslint": "~8.14.0", + "eslint-plugin-jsdoc": "^46.4.6", "eslint-plugin-vue": "~8.7.1", "favico.js": "~0.3.10", "jest": "~29.6.1", @@ -180,6 +184,7 @@ "stylelint": "^15.10.1", "stylelint-config-standard": "~25.0.0", "terser": "~5.15.0", + "test": "~3.3.0", "timezones-list": "~3.0.1", "typescript": "~4.4.4", "v-pagination-3": "~0.1.7", diff --git a/server/auth.js b/server/auth.js index 1ea8b45f9..5cf96b6ff 100644 --- a/server/auth.js +++ b/server/auth.js @@ -9,9 +9,9 @@ const dayjs = require("dayjs"); /** * Login to web app - * @param {string} username - * @param {string} password - * @returns {Promise<(Bean|null)>} + * @param {string} username Username to login with + * @param {string} password Password to login with + * @returns {Promise<(Bean|null)>} User or null if login failed */ exports.login = async function (username, password) { if (typeof username !== "string" || typeof password !== "string") { @@ -39,6 +39,7 @@ exports.login = async function (username, password) { /** * Validate a provided API key * @param {string} key API key to verify + * @returns {boolean} API is ok? */ async function verifyAPIKey(key) { if (typeof key !== "string") { @@ -73,9 +74,10 @@ async function verifyAPIKey(key) { /** * Custom authorizer for express-basic-auth - * @param {string} username - * @param {string} password - * @param {authCallback} callback + * @param {string} username Username to login with + * @param {string} password Password to login with + * @param {authCallback} callback Callback to handle login result + * @returns {void} */ function apiAuthorizer(username, password, callback) { // API Rate Limit @@ -99,9 +101,10 @@ function apiAuthorizer(username, password, callback) { /** * Custom authorizer for express-basic-auth - * @param {string} username - * @param {string} password - * @param {authCallback} callback + * @param {string} username Username to login with + * @param {string} password Password to login with + * @param {authCallback} callback Callback to handle login result + * @returns {void} */ function userAuthorizer(username, password, callback) { // Login Rate Limit @@ -126,7 +129,8 @@ function userAuthorizer(username, password, callback) { * Use basic auth if auth is not disabled * @param {express.Request} req Express request object * @param {express.Response} res Express response object - * @param {express.NextFunction} next + * @param {express.NextFunction} next Next handler in chain + * @returns {void} */ exports.basicAuth = async function (req, res, next) { const middleware = basicAuth({ @@ -148,7 +152,8 @@ exports.basicAuth = async function (req, res, next) { * Use use API Key if API keys enabled, else use basic auth * @param {express.Request} req Express request object * @param {express.Response} res Express response object - * @param {express.NextFunction} next + * @param {express.NextFunction} next Next handler in chain + * @returns {void} */ exports.apiAuth = async function (req, res, next) { if (!await Settings.get("disableAuth")) { diff --git a/server/cacheable-dns-http-agent.js b/server/cacheable-dns-http-agent.js index cc067f72b..bcc955689 100644 --- a/server/cacheable-dns-http-agent.js +++ b/server/cacheable-dns-http-agent.js @@ -15,6 +15,7 @@ class CacheableDnsHttpAgent { /** * Register/Disable cacheable to global agents + * @returns {void} */ static async update() { log.debug("CacheableDnsHttpAgent", "update"); @@ -40,14 +41,15 @@ class CacheableDnsHttpAgent { /** * Attach cacheable to HTTP agent * @param {http.Agent} agent Agent to install + * @returns {void} */ static install(agent) { this.cacheable.install(agent); } /** - * @var {https.AgentOptions} agentOptions - * @return {https.Agent} + * @param {https.AgentOptions} agentOptions Options to pass to HTTPS agent + * @returns {https.Agent} The new HTTPS agent */ static getHttpsAgent(agentOptions) { if (!this.enable) { @@ -63,8 +65,8 @@ class CacheableDnsHttpAgent { } /** - * @var {http.AgentOptions} agentOptions - * @return {https.Agents} + * @param {http.AgentOptions} agentOptions Options to pass to the HTTP agent + * @returns {https.Agents} The new HTTP agent */ static getHttpAgent(agentOptions) { if (!this.enable) { diff --git a/server/client.js b/server/client.js index 7891dd166..d03065f36 100644 --- a/server/client.js +++ b/server/client.js @@ -12,7 +12,7 @@ const checkVersion = require("./check-version"); /** * Send list of notification providers to client * @param {Socket} socket Socket.io socket instance - * @returns {Promise} + * @returns {Promise} List of notifications */ async function sendNotificationList(socket) { const timeLogger = new TimeLogger(); @@ -40,13 +40,11 @@ async function sendNotificationList(socket) { * Send Heartbeat History list to socket * @param {Socket} socket Socket.io instance * @param {number} monitorID ID of monitor to send heartbeat history - * @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only - * @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list + * @param {boolean} toUser True = send to all browsers with the same user id, False = send to the current browser only + * @param {boolean} overwrite Overwrite client-side's heartbeat list * @returns {Promise} */ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { - const timeLogger = new TimeLogger(); - let list = await R.getAll(` SELECT * FROM heartbeat WHERE monitor_id = ? @@ -63,16 +61,14 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = } else { socket.emit("heartbeatList", monitorID, result, overwrite); } - - timeLogger.print(`[Monitor: ${monitorID}] sendHeartbeatList`); } /** * Important Heart beat list (aka event list) * @param {Socket} socket Socket.io instance * @param {number} monitorID ID of monitor to send heartbeat history - * @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only - * @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list + * @param {boolean} toUser True = send to all browsers with the same user id, False = send to the current browser only + * @param {boolean} overwrite Overwrite client-side's heartbeat list * @returns {Promise} */ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { @@ -100,7 +96,7 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove /** * Emit proxy list to client * @param {Socket} socket Socket.io socket instance - * @return {Promise} + * @returns {Promise} List of proxies */ async function sendProxyList(socket) { const timeLogger = new TimeLogger(); @@ -141,7 +137,7 @@ async function sendAPIKeyList(socket) { /** * Emits the version information to the client. * @param {Socket} socket Socket.io socket instance - * @param {boolean} hideVersion + * @param {boolean} hideVersion Should we hide the version information in the response? * @returns {Promise} */ async function sendInfo(socket, hideVersion = false) { @@ -168,7 +164,7 @@ async function sendInfo(socket, hideVersion = false) { /** * Send list of docker hosts to client * @param {Socket} socket Socket.io socket instance - * @returns {Promise} + * @returns {Promise} List of docker hosts */ async function sendDockerHostList(socket) { const timeLogger = new TimeLogger(); diff --git a/server/database.js b/server/database.js index c00f950be..736d7043c 100644 --- a/server/database.js +++ b/server/database.js @@ -4,6 +4,8 @@ const { setSetting, setting } = require("./util-server"); const { log, sleep } = require("../src/util"); const knex = require("knex"); const path = require("path"); +const { EmbeddedMariaDB } = require("./embedded-mariadb"); +const mysql = require("mysql2/promise"); /** * Database & App Data Folder @@ -24,7 +26,7 @@ class Database { static screenshotDir; - static path; + static sqlitePath; static dockerTLSDir; @@ -34,11 +36,13 @@ class Database { static patched = false; /** + * SQLite only * Add patch filename in key * Values: * true: Add it regardless of order * false: Do nothing * { parents: []}: Need parents before add it + * @deprecated */ static patchList = { "patch-setting-value-type.sql": true, @@ -80,7 +84,7 @@ class Database { "patch-add-certificate-expiry-status-page.sql": true, "patch-monitor-oauth-cc.sql": true, "patch-add-timeout-monitor.sql": true, - "patch-add-gamedig-given-port.sql": true, + "patch-add-gamedig-given-port.sql": true, // The last file so far converted to a knex migration file }; /** @@ -91,15 +95,20 @@ class Database { static noReject = true; + static dbConfig = {}; + + static knexMigrationsPath = "./db/knex_migrations"; + /** - * Initialize the database - * @param {Object} args Arguments to initialize DB with + * Initialize the data directory + * @param {object} args Arguments to initialize DB with + * @returns {void} */ - static init(args) { + static initDataDir(args) { // Data Directory (must be end with "/") Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/"; - Database.path = path.join(Database.dataDir, "kuma.db"); + Database.sqlitePath = path.join(Database.dataDir, "kuma.db"); if (! fs.existsSync(Database.dataDir)) { fs.mkdirSync(Database.dataDir, { recursive: true }); } @@ -124,36 +133,143 @@ class Database { log.info("db", `Data Dir: ${Database.dataDir}`); } + /** + * + */ + static readDBConfig() { + let dbConfig; + + let dbConfigString = fs.readFileSync(path.join(Database.dataDir, "db-config.json")).toString("utf-8"); + dbConfig = JSON.parse(dbConfigString); + + if (typeof dbConfig !== "object") { + throw new Error("Invalid db-config.json, it must be an object"); + } + + if (typeof dbConfig.type !== "string") { + throw new Error("Invalid db-config.json, type must be a string"); + } + return dbConfig; + } + + /** + * @param dbConfig + */ + static writeDBConfig(dbConfig) { + fs.writeFileSync(path.join(Database.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4)); + } + /** * Connect to the database - * @param {boolean} [testMode=false] Should the connection be + * @param {boolean} testMode Should the connection be * started in test mode? - * @param {boolean} [autoloadModels=true] Should models be + * @param {boolean} autoloadModels Should models be * automatically loaded? - * @param {boolean} [noLog=false] Should logs not be output? + * @param {boolean} noLog Should logs not be output? * @returns {Promise} */ static async connect(testMode = false, autoloadModels = true, noLog = false) { const acquireConnectionTimeout = 120 * 1000; + let dbConfig; + try { + dbConfig = this.readDBConfig(); + Database.dbConfig = dbConfig; + } catch (err) { + log.warn("db", err.message); + dbConfig = { + type: "sqlite", + }; + } - const Dialect = require("knex/lib/dialects/sqlite3/index.js"); - Dialect.prototype._driver = () => require("@louislam/sqlite3"); + let config = {}; + + let mariadbPoolConfig = { + afterCreate: function (conn, done) { - const knexInstance = knex({ - client: Dialect, - connection: { - filename: Database.path, - acquireConnectionTimeout: acquireConnectionTimeout, - }, - useNullAsDefault: true, - pool: { - min: 1, - max: 1, - idleTimeoutMillis: 120 * 1000, - propagateCreateError: false, - acquireTimeoutMillis: acquireConnectionTimeout, } - }); + }; + + log.info("db", `Database Type: ${dbConfig.type}`); + + if (dbConfig.type === "sqlite") { + + if (! fs.existsSync(Database.sqlitePath)) { + log.info("server", "Copying Database"); + fs.copyFileSync(Database.templatePath, Database.sqlitePath); + } + + const Dialect = require("knex/lib/dialects/sqlite3/index.js"); + Dialect.prototype._driver = () => require("@louislam/sqlite3"); + + config = { + client: Dialect, + connection: { + filename: Database.sqlitePath, + acquireConnectionTimeout: acquireConnectionTimeout, + }, + useNullAsDefault: true, + pool: { + min: 1, + max: 1, + idleTimeoutMillis: 120 * 1000, + propagateCreateError: false, + acquireTimeoutMillis: acquireConnectionTimeout, + } + }; + } else if (dbConfig.type === "mariadb") { + if (!/^\w+$/.test(dbConfig.dbName)) { + throw Error("Invalid database name. A database name can only consist of letters, numbers and underscores"); + } + + const connection = await mysql.createConnection({ + host: dbConfig.hostname, + port: dbConfig.port, + user: dbConfig.username, + password: dbConfig.password, + }); + + await connection.execute("CREATE DATABASE IF NOT EXISTS " + dbConfig.dbName + " CHARACTER SET utf8mb4"); + connection.end(); + + config = { + client: "mysql2", + connection: { + host: dbConfig.hostname, + port: dbConfig.port, + user: dbConfig.username, + password: dbConfig.password, + database: dbConfig.dbName, + timezone: "UTC", + }, + pool: mariadbPoolConfig, + }; + } else if (dbConfig.type === "embedded-mariadb") { + let embeddedMariaDB = EmbeddedMariaDB.getInstance(); + await embeddedMariaDB.start(); + log.info("mariadb", "Embedded MariaDB started"); + config = { + client: "mysql2", + connection: { + socketPath: embeddedMariaDB.socketPath, + user: "node", + database: "kuma", + }, + pool: mariadbPoolConfig, + }; + } else { + throw new Error("Unknown Database type: " + dbConfig.type); + } + + // Set to utf8mb4 for MariaDB + if (dbConfig.type.endsWith("mariadb")) { + config.pool = { + afterCreate(conn, done) { + conn.query("SET CHARACTER SET utf8mb4;", (err) => done(err, conn)); + }, + }; + } + + const knexInstance = knex(config); R.setup(knexInstance); @@ -168,6 +284,18 @@ class Database { await R.autoloadModels("./server/model"); } + if (dbConfig.type === "sqlite") { + await this.initSQLite(testMode, noLog); + } else if (dbConfig.type.endsWith("mariadb")) { + await this.initMariaDB(); + } + } + + /** + * @param testMode + * @param noLog + */ + static async initSQLite(testMode, noLog) { await R.exec("PRAGMA foreign_keys = ON"); if (testMode) { // Change to MEMORY @@ -192,8 +320,57 @@ class Database { } } - /** Patch the database */ + /** + * + */ + static async initMariaDB() { + log.debug("db", "Checking if MariaDB database exists..."); + + let hasTable = await R.hasTable("docker_host"); + if (!hasTable) { + const { createTables } = require("../db/knex_init_db"); + await createTables(); + } else { + log.debug("db", "MariaDB database already exists"); + } + } + + /** + * Patch the database + * @returns {void} + */ static async patch() { + // Still need to keep this for old versions of Uptime Kuma + if (Database.dbConfig.type === "sqlite") { + await this.patchSqlite(); + } + + // Using knex migrations + // https://knexjs.org/guide/migrations.html + // https://gist.github.com/NigelEarle/70db130cc040cc2868555b29a0278261 + try { + await R.knex.migrate.latest({ + directory: Database.knexMigrationsPath, + }); + } catch (e) { + log.error("db", "Database migration failed"); + throw e; + } + } + + /** + * TODO + * @returns {Promise} + */ + static async rollbackLatestPatch() { + + } + + /** + * Patch the database for SQLite + * @deprecated + */ + static async patchSqlite() { let version = parseInt(await setting("database_version")); if (! version) { @@ -213,7 +390,7 @@ class Database { // Try catch anything here try { for (let i = version + 1; i <= this.latestVersion; i++) { - const sqlFile = `./db/patch${i}.sql`; + const sqlFile = `./db/old_migrations/patch${i}.sql`; log.info("db", `Patching ${sqlFile}`); await Database.importSQLFile(sqlFile); log.info("db", `Patched ${sqlFile}`); @@ -230,17 +407,18 @@ class Database { } } - await this.patch2(); + await this.patchSqlite2(); await this.migrateNewStatusPage(); } /** * Patch DB using new process * Call it from patch() only + * @deprecated * @private * @returns {Promise} */ - static async patch2() { + static async patchSqlite2() { log.info("db", "Database Patch 2.0 Process"); let databasePatchedFiles = await setting("databasePatchedFiles"); @@ -274,6 +452,7 @@ class Database { } /** + * SQlite only * Migrate status page value in setting to "status_page" table * @returns {Promise} */ @@ -345,8 +524,8 @@ class Database { * Patch database using new patching process * Used it patch2() only * @private - * @param sqlFilename - * @param databasePatchedFiles + * @param {string} sqlFilename Name of SQL file to load + * @param {object} databasePatchedFiles Patch status of database files * @returns {Promise} */ static async patch2Recursion(sqlFilename, databasePatchedFiles) { @@ -370,7 +549,7 @@ class Database { log.info("db", sqlFilename + " is patching"); this.patched = true; - await this.importSQLFile("./db/" + sqlFilename); + await this.importSQLFile("./db/old_migrations/" + sqlFilename); databasePatchedFiles[sqlFilename] = true; log.info("db", sqlFilename + " was patched successfully"); @@ -381,7 +560,7 @@ class Database { /** * Load an SQL file and execute it - * @param filename Filename of SQL file to import + * @param {string} filename Filename of SQL file to import * @returns {Promise} */ static async importSQLFile(filename) { @@ -413,14 +592,6 @@ class Database { } } - /** - * Aquire a direct connection to database - * @returns {any} - */ - static getBetterSQLite3Database() { - return R.knex.client.acquireConnection(); - } - /** * Special handle, because tarn.js throw a promise reject that cannot be caught * @returns {Promise} @@ -434,7 +605,9 @@ class Database { log.info("db", "Closing the database"); // Flush WAL to main database - await R.exec("PRAGMA wal_checkpoint(TRUNCATE)"); + if (Database.dbConfig.type === "sqlite") { + await R.exec("PRAGMA wal_checkpoint(TRUNCATE)"); + } while (true) { Database.noReject = true; @@ -447,17 +620,23 @@ class Database { log.info("db", "Waiting to close the database"); } } - log.info("db", "SQLite closed"); + log.info("db", "Database closed"); process.removeListener("unhandledRejection", listener); } - /** Get the size of the database */ + /** + * Get the size of the database (SQLite only) + * @returns {number} Size of database + */ static getSize() { - log.debug("db", "Database.getSize()"); - let stats = fs.statSync(Database.path); - log.debug("db", stats); - return stats.size; + if (Database.dbConfig.type === "sqlite") { + log.debug("db", "Database.getSize()"); + let stats = fs.statSync(Database.sqlitePath); + log.debug("db", stats); + return stats.size; + } + return 0; } /** @@ -465,8 +644,22 @@ class Database { * @returns {Promise} */ static async shrink() { - await R.exec("VACUUM"); + if (Database.dbConfig.type === "sqlite") { + await R.exec("VACUUM"); + } } + + /** + * + */ + static sqlHourOffset() { + if (this.dbConfig.client === "sqlite3") { + return "DATETIME('now', ? || ' hours')"; + } else { + return "DATE_ADD(NOW(), INTERVAL ? HOUR)"; + } + } + } module.exports = Database; diff --git a/server/docker.js b/server/docker.js index 56f325828..545c14bd6 100644 --- a/server/docker.js +++ b/server/docker.js @@ -14,10 +14,10 @@ class DockerHost { /** * Save a docker host - * @param {Object} dockerHost Docker host to save + * @param {object} dockerHost Docker host to save * @param {?number} dockerHostID ID of the docker host to update * @param {number} userID ID of the user who adds the docker host - * @returns {Promise} + * @returns {Promise} Updated docker host */ static async save(dockerHost, dockerHostID, userID) { let bean; @@ -64,7 +64,7 @@ class DockerHost { /** * Fetches the amount of containers on the Docker host - * @param {Object} dockerHost Docker host to check for + * @param {object} dockerHost Docker host to check for * @returns {number} Total amount of containers on the host */ static async testDockerHost(dockerHost) { @@ -108,6 +108,8 @@ class DockerHost { /** * Since axios 0.27.X, it does not accept `tcp://` protocol. * Change it to `http://` on the fly in order to fix it. (https://github.com/louislam/uptime-kuma/issues/2165) + * @param {any} url URL to fix + * @returns {any} URL with tcp:// replaced by http:// */ static patchDockerURL(url) { if (typeof url === "string") { @@ -129,11 +131,10 @@ class DockerHost { * 'data/docker-tls/example.com/' would be searched for certificate files), * then 'ca.pem', 'key.pem' and 'cert.pem' files are included in the agent options. * File names can also be overridden via 'DOCKER_TLS_FILE_NAME_(CA|KEY|CERT)'. - * - * @param {String} dockerType i.e. "tcp" or "socket" - * @param {String} url The docker host URL rewritten to https:// - * @return {Object} - * */ + * @param {string} dockerType i.e. "tcp" or "socket" + * @param {string} url The docker host URL rewritten to https:// + * @returns {object} HTTP agent options + */ static getHttpsAgentOptions(dockerType, url) { let baseOptions = { maxCachedSessions: 0, diff --git a/server/embedded-mariadb.js b/server/embedded-mariadb.js new file mode 100644 index 000000000..d6395db44 --- /dev/null +++ b/server/embedded-mariadb.js @@ -0,0 +1,168 @@ +const { log } = require("../src/util"); +const childProcess = require("child_process"); +const fs = require("fs"); +const mysql = require("mysql2"); + +/** + * It is only used inside the docker container + */ +class EmbeddedMariaDB { + + static instance = null; + + exec = "mariadbd"; + + mariadbDataDir = "/app/data/mariadb"; + + runDir = "/app/data/run/mariadb"; + + socketPath = this.runDir + "/mysqld.sock"; + + childProcess = null; + running = false; + + started = false; + + /** + * @returns {EmbeddedMariaDB} + */ + static getInstance() { + if (!EmbeddedMariaDB.instance) { + EmbeddedMariaDB.instance = new EmbeddedMariaDB(); + } + return EmbeddedMariaDB.instance; + } + + /** + * + */ + static hasInstance() { + return !!EmbeddedMariaDB.instance; + } + + /** + * + */ + start() { + if (this.childProcess) { + log.info("mariadb", "Already started"); + return; + } + + this.initDB(); + + this.running = true; + log.info("mariadb", "Starting Embedded MariaDB"); + this.childProcess = childProcess.spawn(this.exec, [ + "--user=node", + "--datadir=" + this.mariadbDataDir, + `--socket=${this.socketPath}`, + `--pid-file=${this.runDir}/mysqld.pid`, + ]); + + this.childProcess.on("close", (code) => { + this.running = false; + this.childProcess = null; + this.started = false; + log.info("mariadb", "Stopped Embedded MariaDB: " + code); + + if (code !== 0) { + log.info("mariadb", "Try to restart Embedded MariaDB as it is not stopped by user"); + this.start(); + } + }); + + this.childProcess.on("error", (err) => { + if (err.code === "ENOENT") { + log.error("mariadb", `Embedded MariaDB: ${this.exec} is not found`); + } else { + log.error("mariadb", err); + } + }); + + let handler = (data) => { + log.debug("mariadb", data.toString("utf-8")); + if (data.toString("utf-8").includes("ready for connections")) { + this.initDBAfterStarted(); + } + }; + + this.childProcess.stdout.on("data", handler); + this.childProcess.stderr.on("data", handler); + + return new Promise((resolve) => { + let interval = setInterval(() => { + if (this.started) { + clearInterval(interval); + resolve(); + } else { + log.info("mariadb", "Waiting for Embedded MariaDB to start..."); + } + }, 1000); + }); + } + + /** + * + */ + stop() { + if (this.childProcess) { + this.childProcess.kill("SIGINT"); + this.childProcess = null; + } + } + + /** + * + */ + initDB() { + if (!fs.existsSync(this.mariadbDataDir)) { + log.info("mariadb", `Embedded MariaDB: ${this.mariadbDataDir} is not found, create one now.`); + fs.mkdirSync(this.mariadbDataDir, { + recursive: true, + }); + + let result = childProcess.spawnSync("mysql_install_db", [ + "--user=node", + "--ldata=" + this.mariadbDataDir, + ]); + + if (result.status !== 0) { + let error = result.stderr.toString("utf-8"); + log.error("mariadb", error); + return; + } else { + log.info("mariadb", "Embedded MariaDB: mysql_install_db done:" + result.stdout.toString("utf-8")); + } + } + + if (!fs.existsSync(this.runDir)) { + log.info("mariadb", `Embedded MariaDB: ${this.runDir} is not found, create one now.`); + fs.mkdirSync(this.runDir, { + recursive: true, + }); + } + + } + + /** + * + */ + async initDBAfterStarted() { + const connection = mysql.createConnection({ + socketPath: this.socketPath, + user: "node", + }); + + let result = await connection.execute("CREATE DATABASE IF NOT EXISTS `kuma`"); + log.debug("mariadb", "CREATE DATABASE: " + JSON.stringify(result)); + + log.info("mariadb", "Embedded MariaDB is ready for connections"); + this.started = true; + } + +} + +module.exports = { + EmbeddedMariaDB, +}; diff --git a/server/google-analytics.js b/server/google-analytics.js index fc9fbec84..ceae7d2eb 100644 --- a/server/google-analytics.js +++ b/server/google-analytics.js @@ -3,8 +3,8 @@ const jsesc = require("jsesc"); /** * Returns a string that represents the javascript that is required to insert the Google Analytics scripts * into a webpage. - * @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. - * @returns {string} + * @param {string} tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. + * @returns {string} HTML script tags to inject into page */ function getGoogleAnalyticsScript(tagId) { let escapedTagId = jsesc(tagId, { isScriptContext: true }); diff --git a/server/image-data-uri.js b/server/image-data-uri.js index eaf651f01..77dd23307 100644 --- a/server/image-data-uri.js +++ b/server/image-data-uri.js @@ -10,7 +10,7 @@ let ImageDataURI = (() => { /** * Decode the data:image/ URI * @param {string} dataURI data:image/ URI to decode - * @returns {?Object} An object with properties "imageType" and "dataBase64". + * @returns {?object} An object with properties "imageType" and "dataBase64". * The former is the image type, e.g., "png", and the latter is a base64 * encoded string of the image's binary data. If it fails to parse, returns * null instead of an object. @@ -52,8 +52,8 @@ let ImageDataURI = (() => { /** * Write data URI to file * @param {string} dataURI data:image/ URI - * @param {string} [filePath] Path to write file to - * @returns {Promise} + * @param {string} filePath Path to write file to + * @returns {Promise} Write file error */ function outputFile(dataURI, filePath) { filePath = filePath || "./"; diff --git a/server/jobs.js b/server/jobs.js index a17bd8c9b..0838731d0 100644 --- a/server/jobs.js +++ b/server/jobs.js @@ -39,7 +39,10 @@ const initBackgroundJobs = async function () { }; -/** Stop all background jobs if running */ +/** + * Stop all background jobs if running + * @returns {void} + */ const stopBackgroundJobs = function () { for (const job of jobs) { if (job.croner) { diff --git a/server/jobs/clear-old-data.js b/server/jobs/clear-old-data.js index ad8ea1d38..248a4d409 100644 --- a/server/jobs/clear-old-data.js +++ b/server/jobs/clear-old-data.js @@ -1,12 +1,13 @@ const { R } = require("redbean-node"); const { log } = require("../../src/util"); const { setSetting, setting } = require("../util-server"); +const Database = require("../database"); const DEFAULT_KEEP_PERIOD = 180; /** * Clears old data from the heartbeat table of the database. - * @return {Promise} A promise that resolves when the data has been cleared. + * @returns {Promise} A promise that resolves when the data has been cleared. */ const clearOldData = async () => { @@ -34,13 +35,17 @@ const clearOldData = async () => { log.debug("clearOldData", `Clearing Data older than ${parsedPeriod} days...`); + const sqlHourOffset = Database.sqlHourOffset(); + try { await R.exec( - "DELETE FROM heartbeat WHERE time < DATETIME('now', '-' || ? || ' days') ", - [ parsedPeriod ] + "DELETE FROM heartbeat WHERE time < " + sqlHourOffset, + [ parsedPeriod * -24 ] ); - await R.exec("PRAGMA optimize;"); + if (Database.dbConfig.type === "sqlite") { + await R.exec("PRAGMA optimize;"); + } } catch (e) { log.error("clearOldData", `Failed to clear old data: ${e.message}`); } diff --git a/server/jobs/incremental-vacuum.js b/server/jobs/incremental-vacuum.js index a45232463..f0fa78a17 100644 --- a/server/jobs/incremental-vacuum.js +++ b/server/jobs/incremental-vacuum.js @@ -1,13 +1,19 @@ const { R } = require("redbean-node"); const { log } = require("../../src/util"); +const Database = require("../database"); /** * Run incremental_vacuum and checkpoint the WAL. - * @return {Promise} A promise that resolves when the process is finished. + * @returns {Promise} A promise that resolves when the process is finished. */ const incrementalVacuum = async () => { try { + if (Database.dbConfig.type !== "sqlite") { + log.debug("incrementalVacuum", "Skipping incremental_vacuum, not using SQLite."); + return; + } + log.debug("incrementalVacuum", "Running incremental_vacuum and wal_checkpoint(PASSIVE)..."); await R.exec("PRAGMA incremental_vacuum(200)"); await R.exec("PRAGMA wal_checkpoint(PASSIVE)"); diff --git a/server/model/api_key.js b/server/model/api_key.js index 1b27a60f6..96de98454 100644 --- a/server/model/api_key.js +++ b/server/model/api_key.js @@ -19,7 +19,7 @@ class APIKey extends BeanModel { /** * Returns an object that ready to parse to JSON - * @returns {Object} + * @returns {object} Object ready to parse */ toJSON() { return { @@ -37,7 +37,7 @@ class APIKey extends BeanModel { /** * Returns an object that ready to parse to JSON with sensitive fields * removed - * @returns {Object} + * @returns {object} Object ready to parse */ toPublicJSON() { return { @@ -53,9 +53,9 @@ class APIKey extends BeanModel { /** * Create a new API Key and store it in the database - * @param {Object} key Object sent by client + * @param {object} key Object sent by client * @param {int} userID ID of socket user - * @returns {Promise} + * @returns {Promise} API key */ static async save(key, userID) { let bean; diff --git a/server/model/docker_host.js b/server/model/docker_host.js index 205982922..ceb8f4a3d 100644 --- a/server/model/docker_host.js +++ b/server/model/docker_host.js @@ -3,7 +3,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class DockerHost extends BeanModel { /** * Returns an object that ready to parse to JSON - * @returns {Object} + * @returns {object} Object ready to parse */ toJSON() { return { diff --git a/server/model/group.js b/server/model/group.js index 5b712aceb..98ac2a474 100644 --- a/server/model/group.js +++ b/server/model/group.js @@ -4,10 +4,12 @@ const { R } = require("redbean-node"); class Group extends BeanModel { /** - * Return an object that ready to parse to JSON for public - * Only show necessary data to public - * @param {boolean} [showTags=false] Should the JSON include monitor tags - * @returns {Object} + * Return an object that ready to parse to JSON for public Only show + * necessary data to public + * @param {boolean} showTags Should the JSON include monitor tags + * @param {boolean} certExpiry Should JSON include info about + * certificate expiry? + * @returns {object} Object ready to parse */ async toPublicJSON(showTags = false, certExpiry = false) { let monitorBeanList = await this.getMonitorList(); @@ -27,7 +29,7 @@ class Group extends BeanModel { /** * Get all monitors - * @returns {Bean[]} + * @returns {Bean[]} List of monitors */ async getMonitorList() { return R.convertToBeans("monitor", await R.getAll(` diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js index b7847f879..cc86ef634 100644 --- a/server/model/heartbeat.js +++ b/server/model/heartbeat.js @@ -12,7 +12,7 @@ class Heartbeat extends BeanModel { /** * Return an object that ready to parse to JSON for public * Only show necessary data to public - * @returns {Object} + * @returns {object} Object ready to parse */ toPublicJSON() { return { @@ -25,7 +25,7 @@ class Heartbeat extends BeanModel { /** * Return an object that ready to parse to JSON - * @returns {Object} + * @returns {object} Object ready to parse */ toJSON() { return { diff --git a/server/model/incident.js b/server/model/incident.js index f954cc74a..c47dabb41 100644 --- a/server/model/incident.js +++ b/server/model/incident.js @@ -5,7 +5,7 @@ class Incident extends BeanModel { /** * Return an object that ready to parse to JSON for public * Only show necessary data to public - * @returns {Object} + * @returns {object} Object ready to parse */ toPublicJSON() { return { diff --git a/server/model/maintenance.js b/server/model/maintenance.js index f18eb026d..fb51060e3 100644 --- a/server/model/maintenance.js +++ b/server/model/maintenance.js @@ -11,7 +11,7 @@ class Maintenance extends BeanModel { /** * Return an object that ready to parse to JSON for public * Only show necessary data to public - * @returns {Object} + * @returns {object} Object ready to parse */ async toPublicJSON() { @@ -98,7 +98,7 @@ class Maintenance extends BeanModel { /** * Return an object that ready to parse to JSON * @param {string} timezone If not specified, the timeRange will be in UTC - * @returns {Object} + * @returns {object} Object ready to parse */ async toJSON(timezone = null) { return this.toPublicJSON(timezone); @@ -142,7 +142,7 @@ class Maintenance extends BeanModel { /** * Convert data from socket to bean * @param {Bean} bean Bean to fill in - * @param {Object} obj Data to fill bean with + * @param {object} obj Data to fill bean with * @returns {Bean} Filled bean */ static async jsonToBean(bean, obj) { @@ -188,7 +188,7 @@ class Maintenance extends BeanModel { /** * Throw error if cron is invalid - * @param cron + * @param {string|Date} cron Pattern or date * @returns {Promise} */ static async validateCron(cron) { @@ -198,6 +198,8 @@ class Maintenance extends BeanModel { /** * Run the cron + * @param {boolean} throwError Should an error be thrown on failure + * @returns {Promise} */ async run(throwError = false) { if (this.beanMeta.job) { @@ -290,6 +292,10 @@ class Maintenance extends BeanModel { } } + /** + * Get timeslots where maintenance is running + * @returns {object|null} Maintenance time slot + */ getRunningTimeslot() { let start = dayjs(this.beanMeta.job.nextRun(dayjs().add(-this.duration, "second").toDate())); let end = start.add(this.duration, "second"); @@ -305,6 +311,10 @@ class Maintenance extends BeanModel { } } + /** + * Stop the maintenance + * @returns {void} + */ stop() { if (this.beanMeta.job) { this.beanMeta.job.stop(); @@ -312,10 +322,18 @@ class Maintenance extends BeanModel { } } + /** + * Is this maintenance currently active + * @returns {boolean} The maintenance is active? + */ async isUnderMaintenance() { return (await this.getStatus()) === "under-maintenance"; } + /** + * Get the timezone of the maintenance + * @returns {string} timezone + */ async getTimezone() { if (!this.timezone || this.timezone === "SAME_AS_SERVER") { return await UptimeKumaServer.getInstance().getTimezone(); @@ -323,10 +341,18 @@ class Maintenance extends BeanModel { return this.timezone; } + /** + * Get offset for timezone + * @returns {string} offset + */ async getTimezoneOffset() { return dayjs.tz(dayjs(), await this.getTimezone()).format("Z"); } + /** + * Get the current status of the maintenance + * @returns {string} Current status + */ async getStatus() { if (!this.active) { return "inactive"; diff --git a/server/model/monitor.js b/server/model/monitor.js index 1cea35533..461b97e58 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -18,10 +18,10 @@ const apicache = require("../modules/apicache"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const { CacheableDnsHttpAgent } = require("../cacheable-dns-http-agent"); const { DockerHost } = require("../docker"); -const { UptimeCacheList } = require("../uptime-cache-list"); const Gamedig = require("gamedig"); const jsonata = require("jsonata"); const jwt = require("jsonwebtoken"); +const { UptimeCalculator } = require("../uptime-calculator"); /** * status: @@ -33,9 +33,12 @@ const jwt = require("jsonwebtoken"); class Monitor extends BeanModel { /** - * Return an object that ready to parse to JSON for public - * Only show necessary data to public - * @returns {Object} + * Return an object that ready to parse to JSON for public Only show + * necessary data to public + * @param {boolean} showTags Include tags in JSON + * @param {boolean} certExpiry Include certificate expiry info in + * JSON + * @returns {object} Object ready to parse */ async toPublicJSON(showTags = false, certExpiry = false) { let obj = { @@ -64,7 +67,9 @@ class Monitor extends BeanModel { /** * Return an object that ready to parse to JSON - * @returns {Object} + * @param {boolean} includeSensitiveData Include sensitive data in + * JSON + * @returns {object} Object ready to parse */ async toJSON(includeSensitiveData = true) { @@ -182,9 +187,9 @@ class Monitor extends BeanModel { } /** - * Checks if the monitor is active based on itself and its parents - * @returns {Promise} - */ + * Checks if the monitor is active based on itself and its parents + * @returns {Promise} Is the monitor active? + */ async isActive() { const parentActive = await Monitor.isParentActive(this.id); @@ -193,7 +198,8 @@ class Monitor extends BeanModel { /** * Get all tags applied to this monitor - * @returns {Promise[]>} + * @returns {Promise[]>} List of tags on the + * monitor */ async getTags() { return await R.getAll("SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ? ORDER BY tag.name", [ this.id ]); @@ -202,7 +208,8 @@ class Monitor extends BeanModel { /** * Gets certificate expiry for this monitor * @param {number} monitorID ID of monitor to send - * @returns {Promise>} + * @returns {Promise>} Certificate expiry info for + * monitor */ async getCertExpiry(monitorID) { let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ @@ -227,7 +234,9 @@ class Monitor extends BeanModel { /** * Encode user and password to Base64 encoding * for HTTP "basic" auth, as per RFC-7617 - * @returns {string} + * @param {string} user Username to encode + * @param {string} pass Password to encode + * @returns {string} Encoded username:password */ encodeBase64(user, pass) { return Buffer.from(user + ":" + pass).toString("base64"); @@ -235,7 +244,7 @@ class Monitor extends BeanModel { /** * Is the TLS expiry notification enabled? - * @returns {boolean} + * @returns {boolean} Enabled? */ isEnabledExpiryNotification() { return Boolean(this.expiryNotification); @@ -243,7 +252,7 @@ class Monitor extends BeanModel { /** * Parse to boolean - * @returns {boolean} + * @returns {boolean} Should TLS errors be ignored? */ getIgnoreTls() { return Boolean(this.ignoreTls); @@ -251,7 +260,7 @@ class Monitor extends BeanModel { /** * Parse to boolean - * @returns {boolean} + * @returns {boolean} Is the monitor in upside down mode? */ isUpsideDown() { return Boolean(this.upsideDown); @@ -259,7 +268,7 @@ class Monitor extends BeanModel { /** * Parse to boolean - * @returns {boolean} + * @returns {boolean} Invert keyword match? */ isInvertKeyword() { return Boolean(this.invertKeyword); @@ -267,7 +276,7 @@ class Monitor extends BeanModel { /** * Parse to boolean - * @returns {boolean} + * @returns {boolean} Enable TLS for gRPC? */ getGrpcEnableTls() { return Boolean(this.grpcEnableTls); @@ -275,12 +284,15 @@ class Monitor extends BeanModel { /** * Get accepted status codes - * @returns {Object} + * @returns {object} Accepted status codes */ getAcceptedStatuscodes() { return JSON.parse(this.accepted_statuscodes_json); } + /** + * + */ getGameDigGivenPortOnly() { return Boolean(this.gamedigGivenPortOnly); } @@ -288,6 +300,7 @@ class Monitor extends BeanModel { /** * Start monitor * @param {Server} io Socket server instance + * @returns {void} */ start(io) { let previousBeat = null; @@ -332,13 +345,6 @@ class Monitor extends BeanModel { bean.status = flipStatus(bean.status); } - // Duration - if (!isFirstBeat) { - bean.duration = dayjs(bean.time).diff(dayjs(previousBeat.time), "second"); - } else { - bean.duration = 0; - } - try { if (await Monitor.isUnderMaintenance(this.id)) { bean.msg = "Monitor under maintenance"; @@ -957,11 +963,17 @@ class Monitor extends BeanModel { log.warn("monitor", `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`); } + // Calculate uptime + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(this.id); + let endTimeDayjs = await uptimeCalculator.update(bean.status, parseFloat(bean.ping)); + bean.end_time = R.isoDateTimeMillis(endTimeDayjs); + + // Send to frontend log.debug("monitor", `[${this.name}] Send to socket`); - UptimeCacheList.clearCache(this.id); io.to(this.user_id).emit("heartbeat", bean.toJSON()); Monitor.sendStats(io, this.id, this.user_id); + // Store to database log.debug("monitor", `[${this.name}] Store`); await R.store(bean); @@ -979,7 +991,10 @@ class Monitor extends BeanModel { }; - /** Get a heartbeat and handle errors */ + /** + * Get a heartbeat and handle errors7 + * @returns {void} + */ const safeBeat = async () => { try { await beat(); @@ -1007,10 +1022,10 @@ class Monitor extends BeanModel { /** * Make a request using axios - * @param {Object} options Options for Axios + * @param {object} options Options for Axios * @param {boolean} finalCall Should this be the final call i.e - * don't retry on faliure - * @returns {Object} Axios response + * don't retry on failure + * @returns {object} Axios response */ async makeAxiosRequest(options, finalCall = false) { try { @@ -1045,7 +1060,10 @@ class Monitor extends BeanModel { } } - /** Stop monitor */ + /** + * Stop monitor + * @returns {void} + */ stop() { clearTimeout(this.heartbeatInterval); this.isStop = true; @@ -1055,7 +1073,7 @@ class Monitor extends BeanModel { /** * Get prometheus instance - * @returns {Prometheus|undefined} + * @returns {Prometheus|undefined} Current prometheus instance */ getPrometheus() { return this.prometheus; @@ -1065,7 +1083,7 @@ class Monitor extends BeanModel { * Helper Method: * returns URL object for further usage * returns null if url is invalid - * @returns {(null|URL)} + * @returns {(null|URL)} Monitor URL */ getUrl() { try { @@ -1077,8 +1095,8 @@ class Monitor extends BeanModel { /** * Store TLS info to database - * @param checkCertificateResult - * @returns {Promise} + * @param {object} checkCertificateResult Certificate to update + * @returns {Promise} Updated certificate */ async updateTlsInfo(checkCertificateResult) { let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ @@ -1125,47 +1143,41 @@ class Monitor extends BeanModel { * @param {Server} io Socket server instance * @param {number} monitorID ID of monitor to send * @param {number} userID ID of user to send to + * @returns {void} */ static async sendStats(io, monitorID, userID) { const hasClients = getTotalClientInRoom(io, userID) > 0; + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); if (hasClients) { - await Monitor.sendAvgPing(24, io, monitorID, userID); - await Monitor.sendUptime(24, io, monitorID, userID); - await Monitor.sendUptime(24 * 30, io, monitorID, userID); + // Send 24 hour average ping + let data24h = await uptimeCalculator.get24Hour(); + io.to(userID).emit("avgPing", monitorID, (data24h.avgPing) ? data24h.avgPing.toFixed(2) : null); + + // Send 24 hour uptime + io.to(userID).emit("uptime", monitorID, 24, data24h.uptime); + + // Send 30 day uptime + let data30d = await uptimeCalculator.get30Day(); + io.to(userID).emit("uptime", monitorID, 720, data30d.uptime); + + // Send 1-year uptime + let data1y = await uptimeCalculator.get1Year(); + io.to(userID).emit("uptime", monitorID, "1y", data1y.uptime); + + // Send Cert Info await Monitor.sendCertInfo(io, monitorID, userID); } else { log.debug("monitor", "No clients in the room, no need to send stats"); } } - /** - * Send the average ping to user - * @param {number} duration Hours - */ - static async sendAvgPing(duration, io, monitorID, userID) { - const timeLogger = new TimeLogger(); - - let avgPing = parseInt(await R.getCell(` - SELECT AVG(ping) - FROM heartbeat - WHERE time > DATETIME('now', ? || ' hours') - AND ping IS NOT NULL - AND monitor_id = ? `, [ - -duration, - monitorID, - ])); - - timeLogger.print(`[Monitor: ${monitorID}] avgPing`); - - io.to(userID).emit("avgPing", monitorID, avgPing); - } - /** * Send certificate information to client * @param {Server} io Socket server instance * @param {number} monitorID ID of monitor to send * @param {number} userID ID of user to send to + * @returns {void} */ static async sendCertInfo(io, monitorID, userID) { let tlsInfo = await R.findOne("monitor_tls_info", "monitor_id = ?", [ @@ -1176,98 +1188,6 @@ class Monitor extends BeanModel { } } - /** - * Uptime with calculation - * Calculation based on: - * https://www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime - * @param {number} duration Hours - * @param {number} monitorID ID of monitor to calculate - */ - static async calcUptime(duration, monitorID, forceNoCache = false) { - - if (!forceNoCache) { - let cachedUptime = UptimeCacheList.getUptime(monitorID, duration); - if (cachedUptime != null) { - return cachedUptime; - } - } - - const timeLogger = new TimeLogger(); - - const startTime = R.isoDateTime(dayjs.utc().subtract(duration, "hour")); - - // Handle if heartbeat duration longer than the target duration - // e.g. If the last beat's duration is bigger that the 24hrs window, it will use the duration between the (beat time - window margin) (THEN case in SQL) - let result = await R.getRow(` - SELECT - -- SUM all duration, also trim off the beat out of time window - SUM( - CASE - WHEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 < duration - THEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 - ELSE duration - END - ) AS total_duration, - - -- SUM all uptime duration, also trim off the beat out of time window - SUM( - CASE - WHEN (status = 1 OR status = 3) - THEN - CASE - WHEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 < duration - THEN (JULIANDAY(\`time\`) - JULIANDAY(?)) * 86400 - ELSE duration - END - END - ) AS uptime_duration - FROM heartbeat - WHERE time > ? - AND monitor_id = ? - `, [ - startTime, startTime, startTime, startTime, startTime, - monitorID, - ]); - - timeLogger.print(`[Monitor: ${monitorID}][${duration}] sendUptime`); - - let totalDuration = result.total_duration; - let uptimeDuration = result.uptime_duration; - let uptime = 0; - - if (totalDuration > 0) { - uptime = uptimeDuration / totalDuration; - if (uptime < 0) { - uptime = 0; - } - - } else { - // Handle new monitor with only one beat, because the beat's duration = 0 - let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [ monitorID ])); - - if (status === UP) { - uptime = 1; - } - } - - // Cache - UptimeCacheList.addUptime(monitorID, duration, uptime); - - return uptime; - } - - /** - * Send Uptime - * @param {number} duration Hours - * @param {Server} io Socket server instance - * @param {number} monitorID ID of monitor to send - * @param {number} userID ID of user to send to - */ - static async sendUptime(duration, io, monitorID, userID) { - const uptime = await this.calcUptime(duration, monitorID); - io.to(userID).emit("uptime", monitorID, duration, uptime); - } - /** * Has status of monitor changed since last beat? * @param {boolean} isFirstBeat Is this the first beat of this monitor? @@ -1336,6 +1256,7 @@ class Monitor extends BeanModel { * @param {boolean} isFirstBeat Is this beat the first of this monitor? * @param {Monitor} monitor The monitor to send a notificaton about * @param {Bean} bean Status information about monitor + * @returns {void} */ static async sendNotification(isFirstBeat, monitor, bean) { if (!isFirstBeat || bean.status === DOWN) { @@ -1376,7 +1297,7 @@ class Monitor extends BeanModel { /** * Get list of notification providers for a given monitor * @param {Monitor} monitor Monitor to get notification providers for - * @returns {Promise[]>} + * @returns {Promise[]>} List of notifications */ static async getNotificationList(monitor) { let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [ @@ -1387,7 +1308,8 @@ class Monitor extends BeanModel { /** * checks certificate chain for expiring certificates - * @param {Object} tlsInfoObject Information about certificate + * @param {object} tlsInfoObject Information about certificate + * @returns {void} */ async checkCertExpiryNotifications(tlsInfoObject) { if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { @@ -1474,7 +1396,7 @@ class Monitor extends BeanModel { /** * Get the status of the previous heartbeat * @param {number} monitorID ID of monitor to check - * @returns {Promise>} + * @returns {Promise>} Previous heartbeat */ static async getPreviousHeartbeat(monitorID) { return await R.getRow(` @@ -1488,7 +1410,7 @@ class Monitor extends BeanModel { /** * Check if monitor is under maintenance * @param {number} monitorID ID of monitor to check - * @returns {Promise} + * @returns {Promise} Is the monitor under maintenance */ static async isUnderMaintenance(monitorID) { const maintenanceIDList = await R.getCol(` @@ -1511,7 +1433,11 @@ class Monitor extends BeanModel { return false; } - /** Make sure monitor interval is between bounds */ + /** + * Make sure monitor interval is between bounds + * @returns {void} + * @throws Interval is outside of range + */ validate() { if (this.interval > MAX_INTERVAL_SECOND) { throw new Error(`Interval cannot be more than ${MAX_INTERVAL_SECOND} seconds`); @@ -1524,7 +1450,7 @@ class Monitor extends BeanModel { /** * Gets Parent of the monitor * @param {number} monitorID ID of monitor to get - * @returns {Promise>} + * @returns {Promise>} Parent */ static async getParent(monitorID) { return await R.getRow(` @@ -1540,7 +1466,7 @@ class Monitor extends BeanModel { /** * Gets all Children of the monitor * @param {number} monitorID ID of monitor to get - * @returns {Promise>} + * @returns {Promise>} Children */ static async getChildren(monitorID) { return await R.getAll(` @@ -1553,7 +1479,7 @@ class Monitor extends BeanModel { /** * Gets Full Path-Name (Groups and Name) - * @returns {Promise} + * @returns {Promise} Full path name of this monitor */ async getPathName() { let path = this.name; @@ -1573,8 +1499,8 @@ class Monitor extends BeanModel { /** * Gets recursive all child ids - * @param {number} monitorID ID of the monitor to get - * @returns {Promise} + * @param {number} monitorID ID of the monitor to get + * @returns {Promise} IDs of all children */ static async getAllChildrenIDs(monitorID) { const childs = await Monitor.getChildren(monitorID); @@ -1605,10 +1531,10 @@ class Monitor extends BeanModel { } /** - * Checks recursive if parent (ancestors) are active - * @param {number} monitorID ID of the monitor to get - * @returns {Promise} - */ + * Checks recursive if parent (ancestors) are active + * @param {number} monitorID ID of the monitor to get + * @returns {Promise} Is the parent monitor active? + */ static async isParentActive(monitorID) { const parent = await Monitor.getParent(monitorID); diff --git a/server/model/proxy.js b/server/model/proxy.js index 4517b11bf..ec78403d4 100644 --- a/server/model/proxy.js +++ b/server/model/proxy.js @@ -3,7 +3,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Proxy extends BeanModel { /** * Return an object that ready to parse to JSON - * @returns {Object} + * @returns {object} Object ready to parse */ toJSON() { return { diff --git a/server/model/status_page.js b/server/model/status_page.js index e168acf2f..e6095a63c 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -14,10 +14,11 @@ class StatusPage extends BeanModel { static domainMappingList = { }; /** - * - * @param {Response} response - * @param {string} indexHTML - * @param {string} slug + * Handle responses to status page + * @param {Response} response Response object + * @param {string} indexHTML HTML to render + * @param {string} slug Status page slug + * @returns {void} */ static async handleStatusPageResponse(response, indexHTML, slug) { let statusPage = await R.findOne("status_page", " slug = ? ", [ @@ -33,8 +34,9 @@ class StatusPage extends BeanModel { /** * SSR for status pages - * @param {string} indexHTML - * @param {StatusPage} statusPage + * @param {string} indexHTML HTML page to render + * @param {StatusPage} statusPage Status page populate HTML with + * @returns {void} */ static async renderHTML(indexHTML, statusPage) { const $ = cheerio.load(indexHTML); @@ -87,7 +89,8 @@ class StatusPage extends BeanModel { /** * Get all status page data in one call - * @param {StatusPage} statusPage + * @param {StatusPage} statusPage Status page to get data for + * @returns {object} Status page data */ static async getStatusPageData(statusPage) { const config = await statusPage.toPublicJSON(); @@ -142,7 +145,7 @@ class StatusPage extends BeanModel { * Send status page list to client * @param {Server} io io Socket server instance * @param {Socket} socket Socket.io instance - * @returns {Promise} + * @returns {Promise} Status page list */ static async sendStatusPageList(io, socket) { let result = {}; @@ -159,7 +162,7 @@ class StatusPage extends BeanModel { /** * Update list of domain names - * @param {string[]} domainNameList + * @param {string[]} domainNameList List of status page domains * @returns {Promise} */ async updateDomainNameList(domainNameList) { @@ -203,7 +206,7 @@ class StatusPage extends BeanModel { /** * Get list of domain names - * @returns {Object[]} + * @returns {object[]} List of status page domains */ getDomainNameList() { let domainList = []; @@ -219,7 +222,7 @@ class StatusPage extends BeanModel { /** * Return an object that ready to parse to JSON - * @returns {Object} + * @returns {object} Object ready to parse */ async toJSON() { return { @@ -243,7 +246,7 @@ class StatusPage extends BeanModel { /** * Return an object that ready to parse to JSON for public * Only show necessary data to public - * @returns {Object} + * @returns {object} Object ready to parse */ async toPublicJSON() { return { @@ -264,7 +267,8 @@ class StatusPage extends BeanModel { /** * Convert slug to status page ID - * @param {string} slug + * @param {string} slug Status page slug + * @returns {Promise} ID of status page */ static async slugToID(slug) { return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [ @@ -274,7 +278,7 @@ class StatusPage extends BeanModel { /** * Get path to the icon for the page - * @returns {string} + * @returns {string} Path */ getIcon() { if (!this.icon) { @@ -287,7 +291,7 @@ class StatusPage extends BeanModel { /** * Get list of maintenances * @param {number} statusPageId ID of status page to get maintenance for - * @returns {Object} Object representing maintenances sanitized for public + * @returns {object} Object representing maintenances sanitized for public */ static async getMaintenanceList(statusPageId) { try { diff --git a/server/model/tag.js b/server/model/tag.js index b31eb8682..bc8a4db19 100644 --- a/server/model/tag.js +++ b/server/model/tag.js @@ -4,7 +4,7 @@ class Tag extends BeanModel { /** * Return an object that ready to parse to JSON - * @returns {Object} + * @returns {object} Object ready to parse */ toJSON() { return { diff --git a/server/model/user.js b/server/model/user.js index fc619c74b..cba53a3f9 100644 --- a/server/model/user.js +++ b/server/model/user.js @@ -7,7 +7,7 @@ class User extends BeanModel { * Reset user password * Fix #1510, as in the context reset-password.js, there is no auto model mapping. Call this static function instead. * @param {number} userID ID of user to update - * @param {string} newPassword + * @param {string} newPassword Users new password * @returns {Promise} */ static async resetPassword(userID, newPassword) { @@ -19,7 +19,7 @@ class User extends BeanModel { /** * Reset this users password - * @param {string} newPassword + * @param {string} newPassword Users new password * @returns {Promise} */ async resetPassword(newPassword) { diff --git a/server/monitor-types/monitor-type.js b/server/monitor-types/monitor-type.js index 80a0a7d92..782bedd5b 100644 --- a/server/monitor-types/monitor-type.js +++ b/server/monitor-types/monitor-type.js @@ -3,10 +3,10 @@ class MonitorType { name = undefined; /** - * - * @param {Monitor} monitor - * @param {Heartbeat} heartbeat - * @param {UptimeKumaServer} server + * Run the monitoring check on the given monitor + * @param {Monitor} monitor Monitor to check + * @param {Heartbeat} heartbeat Monitor heartbeat to update + * @param {UptimeKumaServer} server Uptime Kuma server * @returns {Promise} */ async check(monitor, heartbeat, server) { diff --git a/server/monitor-types/real-browser-monitor-type.js b/server/monitor-types/real-browser-monitor-type.js index f3e5695f8..34ee18482 100644 --- a/server/monitor-types/real-browser-monitor-type.js +++ b/server/monitor-types/real-browser-monitor-type.js @@ -51,6 +51,11 @@ if (process.platform === "win32") { log.debug("chrome", allowedList); +/** + * Is the executable path allowed? + * @param {string} executablePath Path to executable + * @returns {Promise} The executable is allowed? + */ async function isAllowedChromeExecutable(executablePath) { console.log(config.args); if (config.args["allow-all-chrome-exec"] || process.env.UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC === "1") { @@ -61,6 +66,11 @@ async function isAllowedChromeExecutable(executablePath) { return allowedList.includes(executablePath); } +/** + * Get the current instance of the browser. If there isn't one, create + * it. + * @returns {Promise} The browser + */ async function getBrowser() { if (!browser) { let executablePath = await Settings.get("chromeExecutable"); @@ -75,6 +85,11 @@ async function getBrowser() { return browser; } +/** + * Prepare the chrome executable path + * @param {string} executablePath Path to chrome executable + * @returns {Promise} Executable path + */ async function prepareChromeExecutable(executablePath) { // Special code for using the playwright_chromium if (typeof executablePath === "string" && executablePath.toLocaleLowerCase() === "#playwright_chromium") { @@ -121,6 +136,12 @@ async function prepareChromeExecutable(executablePath) { return executablePath; } +/** + * Find the chrome executable + * @param {any[]} executables Executables to search through + * @returns {any} Executable + * @throws Could not find executable + */ function findChrome(executables) { // Use the last working executable, so we don't have to search for it again if (lastAutoDetectChromeExecutable) { @@ -138,6 +159,10 @@ function findChrome(executables) { throw new Error("Chromium not found, please specify Chromium executable path in the settings page."); } +/** + * Reset chrome + * @returns {Promise} + */ async function resetChrome() { if (browser) { await browser.close(); @@ -147,8 +172,8 @@ async function resetChrome() { /** * Test if the chrome executable is valid and return the version - * @param executablePath - * @returns {Promise} + * @param {string} executablePath Path to executable + * @returns {Promise} Chrome version */ async function testChrome(executablePath) { try { @@ -175,6 +200,9 @@ class RealBrowserMonitorType extends MonitorType { name = "real-browser"; + /** + * @inheritdoc + */ async check(monitor, heartbeat, server) { const browser = await getBrowser(); const context = await browser.newContext(); diff --git a/server/monitor-types/tailscale-ping.js b/server/monitor-types/tailscale-ping.js index eeec9e3f3..e5275391a 100644 --- a/server/monitor-types/tailscale-ping.js +++ b/server/monitor-types/tailscale-ping.js @@ -13,9 +13,9 @@ class TailscalePing extends MonitorType { /** * Checks the ping status of the URL associated with the monitor. * It then parses the Tailscale ping command output to update the heatrbeat. - * - * @param {Object} monitor - The monitor object associated with the check. - * @param {Object} heartbeat - The heartbeat object to update. + * @param {object} monitor The monitor object associated with the check. + * @param {object} heartbeat The heartbeat object to update. + * @returns {Promise} * @throws Will throw an error if checking Tailscale ping encounters any error */ async check(monitor, heartbeat) { @@ -31,9 +31,9 @@ class TailscalePing extends MonitorType { /** * Runs the Tailscale ping command to the given URL. - * - * @param {string} hostname - The hostname to ping. - * @returns {Promise} - A Promise that resolves to the output of the Tailscale ping command + * @param {string} hostname The hostname to ping. + * @param {number} interval Interval to send ping + * @returns {Promise} A Promise that resolves to the output of the Tailscale ping command * @throws Will throw an error if the command execution encounters any error. */ async runTailscalePing(hostname, interval) { @@ -61,9 +61,9 @@ class TailscalePing extends MonitorType { /** * Parses the output of the Tailscale ping command to update the heartbeat. - * - * @param {string} tailscaleOutput - The output of the Tailscale ping command. - * @param {Object} heartbeat - The heartbeat object to update. + * @param {string} tailscaleOutput The output of the Tailscale ping command. + * @param {object} heartbeat The heartbeat object to update. + * @returns {void} * @throws Will throw an eror if the output contains any unexpected string. */ parseTailscaleOutput(tailscaleOutput, heartbeat) { diff --git a/server/notification-providers/alerta.js b/server/notification-providers/alerta.js index 2b85d67a6..62092178a 100644 --- a/server/notification-providers/alerta.js +++ b/server/notification-providers/alerta.js @@ -6,6 +6,9 @@ class Alerta extends NotificationProvider { name = "alerta"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/alertnow.js b/server/notification-providers/alertnow.js index d778b01d3..1206b72ec 100644 --- a/server/notification-providers/alertnow.js +++ b/server/notification-providers/alertnow.js @@ -7,6 +7,9 @@ class AlertNow extends NotificationProvider { name = "AlertNow"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index f0530b499..1718bbfe4 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -7,6 +7,9 @@ const qs = require("qs"); class AliyunSMS extends NotificationProvider { name = "AliyunSMS"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; @@ -79,9 +82,9 @@ class AliyunSMS extends NotificationProvider { /** * Aliyun request sign - * @param {Object} param Parameters object to sign + * @param {object} param Parameters object to sign * @param {string} AccessKeySecret Secret key to sign parameters with - * @returns {string} + * @returns {string} Base64 encoded request */ sign(param, AccessKeySecret) { let param2 = {}; @@ -123,7 +126,7 @@ class AliyunSMS extends NotificationProvider { /** * Convert status constant to string * @param {const} status The status constant - * @returns {string} + * @returns {string} Status */ statusToString(status) { switch (status) { diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js index 887afbf55..b624b9101 100644 --- a/server/notification-providers/apprise.js +++ b/server/notification-providers/apprise.js @@ -5,6 +5,9 @@ class Apprise extends NotificationProvider { name = "apprise"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { const args = [ "-vv", "-b", msg, notification.appriseURL ]; if (notification.title) { diff --git a/server/notification-providers/bark.js b/server/notification-providers/bark.js index bcd3c6823..8746761e4 100644 --- a/server/notification-providers/bark.js +++ b/server/notification-providers/bark.js @@ -18,6 +18,9 @@ const successMessage = "Successes!"; class Bark extends NotificationProvider { name = "Bark"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let barkEndpoint = notification.barkEndpoint; @@ -45,8 +48,9 @@ class Bark extends NotificationProvider { /** * Add additional parameter for better on device styles (iOS 15 * optimized) + * @param {BeanModel} notification Notification to send * @param {string} postUrl URL to append parameters to - * @returns {string} + * @returns {string} Additional URL parameters */ appendAdditionalParameters(notification, postUrl) { // set icon to uptime kuma icon, 11kb should be fine @@ -70,7 +74,8 @@ class Bark extends NotificationProvider { /** * Check if result is successful - * @param {Object} result Axios response object + * @param {object} result Axios response object + * @returns {void} * @throws {Error} The status code is not in range 2xx */ checkResult(result) { @@ -84,10 +89,11 @@ class Bark extends NotificationProvider { /** * Send the message + * @param {BeanModel} notification Notification to send * @param {string} title Message title * @param {string} subtitle Message * @param {string} endpoint Endpoint to send request to - * @returns {string} + * @returns {string} Success message */ async postNotification(notification, title, subtitle, endpoint) { // url encode title and subtitle diff --git a/server/notification-providers/clicksendsms.js b/server/notification-providers/clicksendsms.js index 1df053098..5539a6a4a 100644 --- a/server/notification-providers/clicksendsms.js +++ b/server/notification-providers/clicksendsms.js @@ -5,6 +5,9 @@ class ClickSendSMS extends NotificationProvider { name = "clicksendsms"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/dingding.js b/server/notification-providers/dingding.js index cea0b0a1e..be3a2fbc6 100644 --- a/server/notification-providers/dingding.js +++ b/server/notification-providers/dingding.js @@ -6,6 +6,9 @@ const Crypto = require("crypto"); class DingDing extends NotificationProvider { name = "DingDing"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; @@ -39,8 +42,8 @@ class DingDing extends NotificationProvider { /** * Send message to DingDing - * @param {BeanModel} notification - * @param {Object} params Parameters of message + * @param {BeanModel} notification Notification to send + * @param {object} params Parameters of message * @returns {boolean} True if successful else false */ async sendToDingDing(notification, params) { @@ -66,7 +69,7 @@ class DingDing extends NotificationProvider { * DingDing sign * @param {Date} timestamp Timestamp of message * @param {string} secretKey Secret key to sign data with - * @returns {string} + * @returns {string} Base64 encoded signature */ sign(timestamp, secretKey) { return Crypto @@ -78,7 +81,7 @@ class DingDing extends NotificationProvider { /** * Convert status constant to string * @param {const} status The status constant - * @returns {string} + * @returns {string} Status */ statusToString(status) { // TODO: Move to notification-provider.js to avoid repetition in classes diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index 2d7c748d9..f24cd6169 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -6,6 +6,9 @@ class Discord extends NotificationProvider { name = "discord"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/feishu.js b/server/notification-providers/feishu.js index 683a3653d..dd5ce2954 100644 --- a/server/notification-providers/feishu.js +++ b/server/notification-providers/feishu.js @@ -5,6 +5,9 @@ const { DOWN, UP } = require("../../src/util"); class Feishu extends NotificationProvider { name = "Feishu"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; let feishuWebHookUrl = notification.feishuWebHookUrl; diff --git a/server/notification-providers/flashduty.js b/server/notification-providers/flashduty.js index 0d6f69e59..83ef27b5c 100644 --- a/server/notification-providers/flashduty.js +++ b/server/notification-providers/flashduty.js @@ -7,6 +7,9 @@ const successMessage = "Sent Successfully."; class FlashDuty extends NotificationProvider { name = "FlashDuty"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { try { if (heartbeatJSON == null) { @@ -33,12 +36,12 @@ class FlashDuty extends NotificationProvider { this.throwGeneralAxiosError(error); } } + /** * Generate a monitor url from the monitors infomation - * @param {Object} monitorInfo Monitor details - * @returns {string|undefined} + * @param {object} monitorInfo Monitor details + * @returns {string|undefined} Monitor URL */ - genMonitorUrl(monitorInfo) { if (monitorInfo.type === "port" && monitorInfo.port) { return monitorInfo.hostname + ":" + monitorInfo.port; @@ -54,9 +57,9 @@ class FlashDuty extends NotificationProvider { * @param {BeanModel} notification Message title * @param {string} title Message * @param {string} body Message - * @param {Object} monitorInfo Monitor details + * @param {object} monitorInfo Monitor details * @param {string} eventStatus Monitor status (Info, Warning, Critical, Ok) - * @returns {string} + * @returns {string} Success message */ async postNotification(notification, title, body, monitorInfo, eventStatus) { const options = { diff --git a/server/notification-providers/freemobile.js b/server/notification-providers/freemobile.js index 919150fa0..44c55bfd5 100644 --- a/server/notification-providers/freemobile.js +++ b/server/notification-providers/freemobile.js @@ -5,6 +5,9 @@ class FreeMobile extends NotificationProvider { name = "FreeMobile"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/goalert.js b/server/notification-providers/goalert.js index 73e0375ac..abf779241 100644 --- a/server/notification-providers/goalert.js +++ b/server/notification-providers/goalert.js @@ -6,6 +6,9 @@ class GoAlert extends NotificationProvider { name = "GoAlert"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/google-chat.js b/server/notification-providers/google-chat.js index 02cb4823c..413fde26d 100644 --- a/server/notification-providers/google-chat.js +++ b/server/notification-providers/google-chat.js @@ -8,6 +8,9 @@ class GoogleChat extends NotificationProvider { name = "GoogleChat"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/gorush.js b/server/notification-providers/gorush.js index 6d756e46c..28f03ab98 100644 --- a/server/notification-providers/gorush.js +++ b/server/notification-providers/gorush.js @@ -5,6 +5,9 @@ class Gorush extends NotificationProvider { name = "gorush"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/gotify.js b/server/notification-providers/gotify.js index 0c5244939..3e99b3d1a 100644 --- a/server/notification-providers/gotify.js +++ b/server/notification-providers/gotify.js @@ -5,6 +5,9 @@ class Gotify extends NotificationProvider { name = "gotify"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/home-assistant.js b/server/notification-providers/home-assistant.js index 6b9504767..292fc3e67 100644 --- a/server/notification-providers/home-assistant.js +++ b/server/notification-providers/home-assistant.js @@ -6,6 +6,9 @@ const defaultNotificationService = "notify"; class HomeAssistant extends NotificationProvider { name = "HomeAssistant"; + /** + * @inheritdoc + */ async send(notification, message, monitor = null, heartbeat = null) { const notificationService = notification?.notificationService || defaultNotificationService; diff --git a/server/notification-providers/kook.js b/server/notification-providers/kook.js index b37b75ab1..b9b68f436 100644 --- a/server/notification-providers/kook.js +++ b/server/notification-providers/kook.js @@ -5,6 +5,9 @@ class Kook extends NotificationProvider { name = "Kook"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; let url = "https://www.kookapp.cn/api/v3/message/create"; diff --git a/server/notification-providers/line.js b/server/notification-providers/line.js index 10b7f2c9b..73dc962f4 100644 --- a/server/notification-providers/line.js +++ b/server/notification-providers/line.js @@ -6,6 +6,9 @@ class Line extends NotificationProvider { name = "line"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/linenotify.js b/server/notification-providers/linenotify.js index 279acb7d4..dfe7e598a 100644 --- a/server/notification-providers/linenotify.js +++ b/server/notification-providers/linenotify.js @@ -7,6 +7,9 @@ class LineNotify extends NotificationProvider { name = "LineNotify"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/lunasea.js b/server/notification-providers/lunasea.js index 0a5fea7b0..3729b0fe4 100644 --- a/server/notification-providers/lunasea.js +++ b/server/notification-providers/lunasea.js @@ -6,6 +6,9 @@ class LunaSea extends NotificationProvider { name = "lunasea"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; let lunaseaurl = ""; diff --git a/server/notification-providers/matrix.js b/server/notification-providers/matrix.js index 915c772d7..c9baf48bc 100644 --- a/server/notification-providers/matrix.js +++ b/server/notification-providers/matrix.js @@ -6,6 +6,9 @@ const { log } = require("../../src/util"); class Matrix extends NotificationProvider { name = "matrix"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/mattermost.js b/server/notification-providers/mattermost.js index d4997392c..9cbb51bbf 100644 --- a/server/notification-providers/mattermost.js +++ b/server/notification-providers/mattermost.js @@ -6,6 +6,9 @@ class Mattermost extends NotificationProvider { name = "mattermost"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/nostr.js b/server/notification-providers/nostr.js index 2c17840d6..9d93678f2 100644 --- a/server/notification-providers/nostr.js +++ b/server/notification-providers/nostr.js @@ -33,6 +33,9 @@ if (semver.lt(nodeVersion, "16.0.0")) { class Nostr extends NotificationProvider { name = "nostr"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { // All DMs should have same timestamp const createdAt = Math.floor(Date.now() / 1000); @@ -86,6 +89,11 @@ class Nostr extends NotificationProvider { return `${successfulRelays}/${relays.length} relays connected.`; } + /** + * Get the private key for the sender + * @param {string} sender Sender to retrieve key for + * @returns {nip19.DecodeResult} Private key + */ async getPrivateKey(sender) { try { const senderDecodeResult = await nip19.decode(sender); @@ -96,6 +104,11 @@ class Nostr extends NotificationProvider { } } + /** + * Get public keys for recipients + * @param {string} recipients Newline delimited list of recipients + * @returns {nip19.DecodeResult[]} Public keys + */ async getPublicKeys(recipients) { const recipientsList = recipients.split("\n"); const publicKeys = []; diff --git a/server/notification-providers/notification-provider.js b/server/notification-providers/notification-provider.js index 2a182d425..9b4f0bb05 100644 --- a/server/notification-providers/notification-provider.js +++ b/server/notification-providers/notification-provider.js @@ -2,16 +2,16 @@ class NotificationProvider { /** * Notification Provider Name - * @type string + * @type {string} */ name = undefined; /** * Send a notification - * @param {BeanModel} notification + * @param {BeanModel} notification Notification to send * @param {string} msg General Message - * @param {?Object} monitorJSON Monitor details (For Up/Down only) - * @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) * @returns {Promise} Return Successful Message * @throws Error with fail msg */ @@ -22,6 +22,7 @@ class NotificationProvider { /** * Throws an error * @param {any} error The error to throw + * @returns {void} * @throws {any} The error specified */ throwGeneralAxiosError(error) { diff --git a/server/notification-providers/ntfy.js b/server/notification-providers/ntfy.js index 2d8378e57..86fc0e081 100644 --- a/server/notification-providers/ntfy.js +++ b/server/notification-providers/ntfy.js @@ -6,6 +6,9 @@ class Ntfy extends NotificationProvider { name = "ntfy"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/octopush.js b/server/notification-providers/octopush.js index d5c475d3d..ec62cd3ad 100644 --- a/server/notification-providers/octopush.js +++ b/server/notification-providers/octopush.js @@ -5,6 +5,9 @@ class Octopush extends NotificationProvider { name = "octopush"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/onebot.js b/server/notification-providers/onebot.js index 6c62eccb5..ec7fc46d4 100644 --- a/server/notification-providers/onebot.js +++ b/server/notification-providers/onebot.js @@ -5,6 +5,9 @@ class OneBot extends NotificationProvider { name = "OneBot"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/opsgenie.js b/server/notification-providers/opsgenie.js index 39cdec9fb..e2dc38a7c 100644 --- a/server/notification-providers/opsgenie.js +++ b/server/notification-providers/opsgenie.js @@ -68,11 +68,11 @@ class Opsgenie extends NotificationProvider { } /** - * - * @param {BeanModel} notification + * Make POST request to Opsgenie + * @param {BeanModel} notification Notification to send * @param {string} url Request url - * @param {Object} data Request body - * @returns {Promise} + * @param {object} data Request body + * @returns {Promise} Success message */ async post(notification, url, data) { let config = { diff --git a/server/notification-providers/pagerduty.js b/server/notification-providers/pagerduty.js index 86e9a0992..c60d782e7 100644 --- a/server/notification-providers/pagerduty.js +++ b/server/notification-providers/pagerduty.js @@ -39,7 +39,8 @@ class PagerDuty extends NotificationProvider { /** * Check if result is successful, result code should be in range 2xx - * @param {Object} result Axios response object + * @param {object} result Axios response object + * @returns {void} * @throws {Error} The status code is not in range 2xx */ checkResult(result) { @@ -56,9 +57,9 @@ class PagerDuty extends NotificationProvider { * @param {BeanModel} notification Message title * @param {string} title Message title * @param {string} body Message - * @param {Object} monitorInfo Monitor details (For Up/Down only) + * @param {object} monitorInfo Monitor details (For Up/Down only) * @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve) - * @returns {string} + * @returns {Promise} Success message */ async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") { diff --git a/server/notification-providers/pagertree.js b/server/notification-providers/pagertree.js index 8a0c4e368..c7a5338d1 100644 --- a/server/notification-providers/pagertree.js +++ b/server/notification-providers/pagertree.js @@ -32,7 +32,8 @@ class PagerTree extends NotificationProvider { /** * Check if result is successful, result code should be in range 2xx - * @param {Object} result Axios response object + * @param {object} result Axios response object + * @returns {void} * @throws {Error} The status code is not in range 2xx */ checkResult(result) { @@ -48,9 +49,10 @@ class PagerTree extends NotificationProvider { * Send the message * @param {BeanModel} notification Message title * @param {string} title Message title - * @param {Object} monitorJSON Monitor details (For Up/Down only) + * @param {object} monitorJSON Monitor details (For Up/Down only) + * @param {object} heartbeatJSON Heartbeat details (For Up/Down only) * @param {?string} eventAction Action event for PagerTree (create, resolve) - * @returns {string} + * @returns {Promise} Success state */ async postNotification(notification, title, monitorJSON, heartbeatJSON, eventAction = "create") { diff --git a/server/notification-providers/promosms.js b/server/notification-providers/promosms.js index 572a21325..7ec919714 100644 --- a/server/notification-providers/promosms.js +++ b/server/notification-providers/promosms.js @@ -5,6 +5,9 @@ class PromoSMS extends NotificationProvider { name = "promosms"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/pushbullet.js b/server/notification-providers/pushbullet.js index f3d95a85e..9fbd4d4c0 100644 --- a/server/notification-providers/pushbullet.js +++ b/server/notification-providers/pushbullet.js @@ -7,6 +7,9 @@ class Pushbullet extends NotificationProvider { name = "pushbullet"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/pushdeer.js b/server/notification-providers/pushdeer.js index 288137d18..78525a54f 100644 --- a/server/notification-providers/pushdeer.js +++ b/server/notification-providers/pushdeer.js @@ -6,6 +6,9 @@ class PushDeer extends NotificationProvider { name = "PushDeer"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; let endpoint = "/message/push"; diff --git a/server/notification-providers/pushover.js b/server/notification-providers/pushover.js index b333b476e..31f8d885e 100644 --- a/server/notification-providers/pushover.js +++ b/server/notification-providers/pushover.js @@ -5,6 +5,9 @@ class Pushover extends NotificationProvider { name = "pushover"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; let pushoverlink = "https://api.pushover.net/1/messages.json"; diff --git a/server/notification-providers/pushy.js b/server/notification-providers/pushy.js index 1d6e7f32f..afb9e617d 100644 --- a/server/notification-providers/pushy.js +++ b/server/notification-providers/pushy.js @@ -5,6 +5,9 @@ class Pushy extends NotificationProvider { name = "pushy"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/rocket-chat.js b/server/notification-providers/rocket-chat.js index 76b964a3a..13b39cde6 100644 --- a/server/notification-providers/rocket-chat.js +++ b/server/notification-providers/rocket-chat.js @@ -8,6 +8,9 @@ class RocketChat extends NotificationProvider { name = "rocket.chat"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/serverchan.js b/server/notification-providers/serverchan.js index d631c8e61..2315e1c33 100644 --- a/server/notification-providers/serverchan.js +++ b/server/notification-providers/serverchan.js @@ -6,6 +6,9 @@ class ServerChan extends NotificationProvider { name = "ServerChan"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { @@ -23,8 +26,8 @@ class ServerChan extends NotificationProvider { /** * Get the formatted title for message - * @param {?Object} monitorJSON Monitor details (For Up/Down only) - * @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) * @returns {string} Formatted title */ checkStatus(heartbeatJSON, monitorJSON) { diff --git a/server/notification-providers/serwersms.js b/server/notification-providers/serwersms.js index 14fe691a8..76e1ef2c1 100644 --- a/server/notification-providers/serwersms.js +++ b/server/notification-providers/serwersms.js @@ -5,6 +5,9 @@ class SerwerSMS extends NotificationProvider { name = "serwersms"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/signal.js b/server/notification-providers/signal.js index 677208ee6..c445ee825 100644 --- a/server/notification-providers/signal.js +++ b/server/notification-providers/signal.js @@ -5,6 +5,9 @@ class Signal extends NotificationProvider { name = "signal"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 41c2bd02c..d512a7cd8 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -10,7 +10,9 @@ class Slack extends NotificationProvider { /** * Deprecated property notification.slackbutton * Set it as primary base url if this is not yet set. + * @deprecated * @param {string} url The primary base URL to use + * @returns {Promise} */ static async deprecateURL(url) { let currentPrimaryBaseURL = await setting("primaryBaseURL"); @@ -25,6 +27,9 @@ class Slack extends NotificationProvider { } } + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/smsc.js b/server/notification-providers/smsc.js index 251bc4554..34d9c8fa0 100644 --- a/server/notification-providers/smsc.js +++ b/server/notification-providers/smsc.js @@ -4,6 +4,9 @@ const axios = require("axios"); class SMSC extends NotificationProvider { name = "smsc"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/smseagle.js b/server/notification-providers/smseagle.js index c431297ec..988f17fdb 100644 --- a/server/notification-providers/smseagle.js +++ b/server/notification-providers/smseagle.js @@ -5,6 +5,9 @@ class SMSEagle extends NotificationProvider { name = "SMSEagle"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/smsmanager.js b/server/notification-providers/smsmanager.js index 833bff60f..7d55d4107 100644 --- a/server/notification-providers/smsmanager.js +++ b/server/notification-providers/smsmanager.js @@ -5,6 +5,9 @@ class SMSManager extends NotificationProvider { name = "SMSManager"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { try { let data = { diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js index 9b59ec09b..170726250 100644 --- a/server/notification-providers/smtp.js +++ b/server/notification-providers/smtp.js @@ -6,6 +6,9 @@ class SMTP extends NotificationProvider { name = "smtp"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { const config = { diff --git a/server/notification-providers/splunk.js b/server/notification-providers/splunk.js index 2d82dd397..e07c51039 100644 --- a/server/notification-providers/splunk.js +++ b/server/notification-providers/splunk.js @@ -37,7 +37,8 @@ class Splunk extends NotificationProvider { /** * Check if result is successful, result code should be in range 2xx - * @param {Object} result Axios response object + * @param {object} result Axios response object + * @returns {void} * @throws {Error} The status code is not in range 2xx */ checkResult(result) { @@ -54,9 +55,9 @@ class Splunk extends NotificationProvider { * @param {BeanModel} notification Message title * @param {string} title Message title * @param {string} body Message - * @param {Object} monitorInfo Monitor details (For Up/Down only) + * @param {object} monitorInfo Monitor details (For Up/Down only) * @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve) - * @returns {string} + * @returns {Promise} Success state */ async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") { diff --git a/server/notification-providers/squadcast.js b/server/notification-providers/squadcast.js index 15553ff7e..0b60503df 100644 --- a/server/notification-providers/squadcast.js +++ b/server/notification-providers/squadcast.js @@ -6,6 +6,9 @@ class Squadcast extends NotificationProvider { name = "squadcast"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/stackfield.js b/server/notification-providers/stackfield.js index 7f22634e3..26c038ea7 100644 --- a/server/notification-providers/stackfield.js +++ b/server/notification-providers/stackfield.js @@ -7,6 +7,9 @@ class Stackfield extends NotificationProvider { name = "stackfield"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null) { let okMsg = "Sent Successfully."; try { diff --git a/server/notification-providers/teams.js b/server/notification-providers/teams.js index bcfc82c24..30976cf5b 100644 --- a/server/notification-providers/teams.js +++ b/server/notification-providers/teams.js @@ -9,7 +9,7 @@ class Teams extends NotificationProvider { * Generate the message to send * @param {const} status The status constant * @param {string} monitorName Name of monitor - * @returns {string} + * @returns {string} Status message */ _statusMessageFactory = (status, monitorName) => { if (status === DOWN) { @@ -37,11 +37,12 @@ class Teams extends NotificationProvider { /** * Generate payload for notification - * @param {const} status The status of the monitor - * @param {string} monitorMessage Message to send - * @param {string} monitorName Name of monitor affected - * @param {string} monitorUrl URL of monitor affected - * @returns {Object} + * @param {object} args Method arguments + * @param {const} args.status The status of the monitor + * @param {string} args.monitorMessage Message to send + * @param {string} args.monitorName Name of monitor affected + * @param {string} args.monitorUrl URL of monitor affected + * @returns {object} Notification payload */ _notificationPayloadFactory = ({ status, @@ -96,7 +97,8 @@ class Teams extends NotificationProvider { /** * Send the notification * @param {string} webhookUrl URL to send the request to - * @param {Object} payload Payload generated by _notificationPayloadFactory + * @param {object} payload Payload generated by _notificationPayloadFactory + * @returns {Promise} */ _sendNotification = async (webhookUrl, payload) => { await axios.post(webhookUrl, payload); @@ -116,6 +118,9 @@ class Teams extends NotificationProvider { return this._sendNotification(webhookUrl, payload); }; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/techulus-push.js b/server/notification-providers/techulus-push.js index 751ff4c3a..bfd9503b9 100644 --- a/server/notification-providers/techulus-push.js +++ b/server/notification-providers/techulus-push.js @@ -5,6 +5,9 @@ class TechulusPush extends NotificationProvider { name = "PushByTechulus"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/telegram.js b/server/notification-providers/telegram.js index 561cabd87..210e3512a 100644 --- a/server/notification-providers/telegram.js +++ b/server/notification-providers/telegram.js @@ -5,6 +5,9 @@ class Telegram extends NotificationProvider { name = "telegram"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/twilio.js b/server/notification-providers/twilio.js index a908fb21e..2abb38b98 100644 --- a/server/notification-providers/twilio.js +++ b/server/notification-providers/twilio.js @@ -5,6 +5,9 @@ class Twilio extends NotificationProvider { name = "twilio"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/webhook.js b/server/notification-providers/webhook.js index 9f5643b4e..597ef8467 100644 --- a/server/notification-providers/webhook.js +++ b/server/notification-providers/webhook.js @@ -7,6 +7,9 @@ class Webhook extends NotificationProvider { name = "webhook"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification-providers/wecom.js b/server/notification-providers/wecom.js index 1825419ad..ea30b26d5 100644 --- a/server/notification-providers/wecom.js +++ b/server/notification-providers/wecom.js @@ -6,6 +6,9 @@ class WeCom extends NotificationProvider { name = "WeCom"; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; @@ -26,9 +29,9 @@ class WeCom extends NotificationProvider { /** * Generate the message to send - * @param {Object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {object} heartbeatJSON Heartbeat details (For Up/Down only) * @param {string} msg General message - * @returns {Object} + * @returns {object} Message */ composeMessage(heartbeatJSON, msg) { let title; diff --git a/server/notification-providers/zoho-cliq.js b/server/notification-providers/zoho-cliq.js index 749647d06..08653e3de 100644 --- a/server/notification-providers/zoho-cliq.js +++ b/server/notification-providers/zoho-cliq.js @@ -10,7 +10,7 @@ class ZohoCliq extends NotificationProvider { * Generate the message to send * @param {const} status The status constant * @param {string} monitorName Name of monitor - * @returns {string} + * @returns {string} Status message */ _statusMessageFactory = (status, monitorName) => { if (status === DOWN) { @@ -25,6 +25,7 @@ class ZohoCliq extends NotificationProvider { * Send the notification * @param {string} webhookUrl URL to send the request to * @param {Array} payload Payload generated by _notificationPayloadFactory + * @returns {Promise} */ _sendNotification = async (webhookUrl, payload) => { await axios.post(webhookUrl, { text: payload.join("\n") }); @@ -32,11 +33,12 @@ class ZohoCliq extends NotificationProvider { /** * Generate payload for notification - * @param {const} status The status of the monitor - * @param {string} monitorMessage Message to send - * @param {string} monitorName Name of monitor affected - * @param {string} monitorUrl URL of monitor affected - * @returns {Array} + * @param {object} args Method arguments + * @param {const} args.status The status of the monitor + * @param {string} args.monitorMessage Message to send + * @param {string} args.monitorName Name of monitor affected + * @param {string} args.monitorUrl URL of monitor affected + * @returns {Array} Notification payload */ _notificationPayloadFactory = ({ status, @@ -74,6 +76,9 @@ class ZohoCliq extends NotificationProvider { return this._sendNotification(webhookUrl, payload); }; + /** + * @inheritdoc + */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let okMsg = "Sent Successfully."; diff --git a/server/notification.js b/server/notification.js index 570c440df..1e7170220 100644 --- a/server/notification.js +++ b/server/notification.js @@ -58,7 +58,12 @@ class Notification { providerList = {}; - /** Initialize the notification providers */ + /** + * Initialize the notification providers + * @returns {void} + * @throws Notification provider does not have a name + * @throws Duplicate notification providers in list + */ static init() { log.info("notification", "Prepare Notification Providers"); @@ -133,10 +138,10 @@ class Notification { /** * Send a notification - * @param {BeanModel} notification + * @param {BeanModel} notification Notification to send * @param {string} msg General Message - * @param {Object} monitorJSON Monitor details (For Up/Down only) - * @param {Object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {object} monitorJSON Monitor details (For Up/Down only) + * @param {object} heartbeatJSON Heartbeat details (For Up/Down only) * @returns {Promise} Successful msg * @throws Error with fail msg */ @@ -150,10 +155,10 @@ class Notification { /** * Save a notification - * @param {Object} notification Notification to save + * @param {object} notification Notification to save * @param {?number} notificationID ID of notification to update * @param {number} userID ID of user who adds notification - * @returns {Promise} + * @returns {Promise} Notification that was saved */ static async save(notification, notificationID, userID) { let bean; diff --git a/server/password-hash.js b/server/password-hash.js index ba228f6b1..83a23d9e6 100644 --- a/server/password-hash.js +++ b/server/password-hash.js @@ -4,8 +4,8 @@ const saltRounds = 10; /** * Hash a password - * @param {string} password - * @returns {string} + * @param {string} password Password to hash + * @returns {string} Hash */ exports.generate = function (password) { return bcrypt.hashSync(password, saltRounds); @@ -13,8 +13,8 @@ exports.generate = function (password) { /** * Verify a password against a hash - * @param {string} password - * @param {string} hash + * @param {string} password Password to verify + * @param {string} hash Hash to verify against * @returns {boolean} Does the password match the hash? */ exports.verify = function (password, hash) { @@ -27,8 +27,8 @@ exports.verify = function (password, hash) { /** * Is the hash a SHA1 hash - * @param {string} hash - * @returns {boolean} + * @param {string} hash Hash to check + * @returns {boolean} Is SHA1 hash? */ function isSHA1(hash) { return (typeof hash === "string" && hash.startsWith("sha1")); @@ -36,7 +36,8 @@ function isSHA1(hash) { /** * Does the hash need to be rehashed? - * @returns {boolean} + * @param {string} hash Hash to check + * @returns {boolean} Needs to be rehashed? */ exports.needRehash = function (hash) { return isSHA1(hash); diff --git a/server/prometheus.js b/server/prometheus.js index dd04394ae..f44af6c1c 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -36,7 +36,7 @@ class Prometheus { monitorLabelValues = {}; /** - * @param {Object} monitor Monitor object to monitor + * @param {object} monitor Monitor object to monitor */ constructor(monitor) { this.monitorLabelValues = { @@ -50,8 +50,9 @@ class Prometheus { /** * Update the metrics page - * @param {Object} heartbeat Heartbeat details - * @param {Object} tlsInfo TLS details + * @param {object} heartbeat Heartbeat details + * @param {object} tlsInfo TLS details + * @returns {void} */ update(heartbeat, tlsInfo) { @@ -99,7 +100,10 @@ class Prometheus { } } - /** Remove monitor from prometheus */ + /** + * Remove monitor from prometheus + * @returns {void} + */ remove() { try { monitorCertDaysRemaining.remove(this.monitorLabelValues); diff --git a/server/proxy.js b/server/proxy.js index 660b9b411..a0b4378d1 100644 --- a/server/proxy.js +++ b/server/proxy.js @@ -11,11 +11,10 @@ class Proxy { /** * Saves and updates given proxy entity - * - * @param proxy - * @param proxyID - * @param userID - * @return {Promise} + * @param {object} proxy Proxy to store + * @param {number} proxyID ID of proxy to update + * @param {number} userID ID of user the proxy belongs to + * @returns {Promise} Updated proxy */ static async save(proxy, proxyID, userID) { let bean; @@ -65,10 +64,9 @@ class Proxy { /** * Deletes proxy with given id and removes it from monitors - * - * @param proxyID - * @param userID - * @return {Promise} + * @param {number} proxyID ID of proxy to delete + * @param {number} userID ID of proxy owner + * @returns {Promise} */ static async delete(proxyID, userID) { const bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [ proxyID, userID ]); @@ -86,10 +84,10 @@ class Proxy { /** * Create HTTP and HTTPS agents related with given proxy bean object - * - * @param proxy proxy bean object - * @param options http and https agent options - * @return {{httpAgent: Agent, httpsAgent: Agent}} + * @param {object} proxy proxy bean object + * @param {object} options http and https agent options + * @returns {{httpAgent: Agent, httpsAgent: Agent}} New HTTP and HTTPS agents + * @throws Proxy protocol is unsupported */ static createAgents(proxy, options) { const { httpAgentOptions, httpsAgentOptions } = options || {}; @@ -171,10 +169,9 @@ class Proxy { /** * Applies given proxy id to monitors - * - * @param proxyID - * @param userID - * @return {Promise} + * @param {number} proxyID ID of proxy to apply + * @param {number} userID ID of proxy owner + * @returns {Promise} */ async function applyProxyEveryMonitor(proxyID, userID) { // Find all monitors with id and proxy id diff --git a/server/rate-limiter.js b/server/rate-limiter.js index ec77f1a4e..3c269b6f0 100644 --- a/server/rate-limiter.js +++ b/server/rate-limiter.js @@ -3,7 +3,7 @@ const { log } = require("../src/util"); class KumaRateLimiter { /** - * @param {Object} config Rate limiter configuration object + * @param {object} config Rate limiter configuration object */ constructor(config) { this.errorMessage = config.errorMessage; @@ -13,14 +13,14 @@ class KumaRateLimiter { /** * Callback for pass * @callback passCB - * @param {Object} err Too many requests + * @param {object} err Too many requests */ /** * Should the request be passed through - * @param {passCB} callback - * @param {number} [num=1] Number of tokens to remove - * @returns {Promise} + * @param {passCB} callback Callback function to call with decision + * @param {number} num Number of tokens to remove + * @returns {Promise} Should the request be allowed? */ async pass(callback, num = 1) { const remainingRequests = await this.removeTokens(num); @@ -39,8 +39,8 @@ class KumaRateLimiter { /** * Remove a given number of tokens - * @param {number} [num=1] Number of tokens to remove - * @returns {Promise} + * @param {number} num Number of tokens to remove + * @returns {Promise} Number of remaining tokens */ async removeTokens(num = 1) { return await this.rateLimiter.removeTokens(num); diff --git a/server/routers/api-router.js b/server/routers/api-router.js index f51f046dd..d480ed6ed 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -7,10 +7,11 @@ const dayjs = require("dayjs"); const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log } = require("../../src/util"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); -const { UptimeCacheList } = require("../uptime-cache-list"); const { makeBadge } = require("badge-maker"); const { badgeConstants } = require("../config"); const { Prometheus } = require("../prometheus"); +const Database = require("../database"); +const { UptimeCalculator } = require("../uptime-calculator"); let router = express.Router(); @@ -88,7 +89,7 @@ router.get("/api/push/:pushToken", async (request, response) => { await R.store(bean); io.to(monitor.user_id).emit("heartbeat", bean.toJSON()); - UptimeCacheList.clearCache(monitor.id); + Monitor.sendStats(io, monitor.id, monitor.user_id); new Prometheus(monitor).update(bean, undefined); @@ -205,9 +206,13 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques try { const requestedMonitorId = parseInt(request.params.id, 10); // if no duration is given, set value to 24 (h) - const requestedDuration = request.params.duration !== undefined ? parseInt(request.params.duration, 10) : 24; + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; const overrideValue = value && parseFloat(value); + if (requestedDuration === "24") { + requestedDuration = "24h"; + } + let publicMonitor = await R.getRow(` SELECT monitor_group.monitor_id FROM monitor_group, \`group\` WHERE monitor_group.group_id = \`group\`.id @@ -224,10 +229,8 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques badgeValues.message = "N/A"; badgeValues.color = badgeConstants.naColor; } else { - const uptime = overrideValue ?? await Monitor.calcUptime( - requestedDuration, - requestedMonitorId - ); + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); + const uptime = overrideValue ?? uptimeCalculator.getDataByDuration(requestedDuration).uptime; // limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits const cleanUptime = (uptime * 100).toPrecision(4); @@ -273,19 +276,19 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const requestedMonitorId = parseInt(request.params.id, 10); // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) - const requestedDuration = Math.min(request.params.duration ? parseInt(request.params.duration, 10) : 24, 720); + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; const overrideValue = value && parseFloat(value); - const publicAvgPing = parseInt(await R.getCell(` - SELECT AVG(ping) FROM monitor_group, \`group\`, heartbeat - WHERE monitor_group.group_id = \`group\`.id - AND heartbeat.time > DATETIME('now', ? || ' hours') - AND heartbeat.ping IS NOT NULL - AND public = 1 - AND heartbeat.monitor_id = ? - `, - [ -requestedDuration, requestedMonitorId ] - )); + if (requestedDuration === "24") { + requestedDuration = "24h"; + } + + const sqlHourOffset = Database.sqlHourOffset(); + + // Check if monitor is public + + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); + const publicAvgPing = uptimeCalculator.getDataByDuration(requestedDuration).avgPing; const badgeValues = { style }; @@ -342,10 +345,12 @@ router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async ( ); const overrideValue = value && parseFloat(value); + const sqlHourOffset = Database.sqlHourOffset(); + const publicAvgPing = parseInt(await R.getCell(` SELECT AVG(ping) FROM monitor_group, \`group\`, heartbeat WHERE monitor_group.group_id = \`group\`.id - AND heartbeat.time > DATETIME('now', ? || ' hours') + AND heartbeat.time > ${sqlHourOffset} AND heartbeat.ping IS NOT NULL AND public = 1 AND heartbeat.monitor_id = ? diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js index b60f286ee..f8c347050 100644 --- a/server/routers/status-page-router.js +++ b/server/routers/status-page-router.js @@ -7,6 +7,7 @@ const { R } = require("redbean-node"); const Monitor = require("../model/monitor"); const { badgeConstants } = require("../config"); const { makeBadge } = require("badge-maker"); +const { UptimeCalculator } = require("../uptime-calculator"); let router = express.Router(); @@ -92,8 +93,8 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques list = R.convertToBeans("heartbeat", list); heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON()); - const type = 24; - uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID); + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); + uptimeList[`${monitorID}_24`] = uptimeCalculator.get24Hour().uptime; } response.json({ diff --git a/server/server.js b/server/server.js index 25a7d4725..d50544bf0 100644 --- a/server/server.js +++ b/server/server.js @@ -84,7 +84,9 @@ log.info("server", "Importing this project modules"); log.debug("server", "Importing Monitor"); const Monitor = require("./model/monitor"); log.debug("server", "Importing Settings"); -const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword, startE2eTests } = require("./util-server"); +const { getSettings, setSettings, setting, initJWTSecret, checkLogin, FBSD, doubleCheckPassword, startE2eTests, + allowDevAllOrigin +} = require("./util-server"); log.debug("server", "Importing Notification"); const { Notification } = require("./notification"); @@ -157,6 +159,8 @@ const { Settings } = require("./settings"); const { CacheableDnsHttpAgent } = require("./cacheable-dns-http-agent"); const apicache = require("./modules/apicache"); const { resetChrome } = require("./monitor-types/real-browser-monitor-type"); +const { EmbeddedMariaDB } = require("./embedded-mariadb"); +const { SetupDatabase } = require("./setup-database"); app.use(express.json()); @@ -176,8 +180,25 @@ app.use(function (req, res, next) { let needSetup = false; (async () => { - Database.init(args); - await initDatabase(testMode); + // Create a data directory + Database.initDataDir(args); + + // Check if is chosen a database type + let setupDatabase = new SetupDatabase(args, server); + if (setupDatabase.isNeedSetup()) { + // Hold here and start a special setup page until user choose a database type + await setupDatabase.start(hostname, port); + } + + // Connect to database + try { + await initDatabase(testMode); + } catch (e) { + log.error("server", "Failed to prepare your database: " + e.message); + process.exit(1); + } + + // Database should be ready now await server.initAfterDatabaseReady(); server.entryPage = await Settings.get("entryPage"); await StatusPage.loadDomainMappingList(); @@ -215,6 +236,14 @@ let needSetup = false; } }); + app.get("/setup-database-info", (request, response) => { + allowDevAllOrigin(response); + response.json({ + runningSetup: false, + needSetup: false, + }); + }); + if (isDev) { app.use(express.urlencoded({ extended: true })); app.post("/test-webhook", async (request, response) => { @@ -342,7 +371,7 @@ let needSetup = false; } // Login Rate Limit - if (! await loginRateLimiter.pass(callback)) { + if (!await loginRateLimiter.pass(callback)) { log.info("auth", `Too many failed requests for user ${data.username}. IP=${clientIP}`); return; } @@ -415,7 +444,7 @@ let needSetup = false; socket.on("logout", async (callback) => { // Rate Limit - if (! await loginRateLimiter.pass(callback)) { + if (!await loginRateLimiter.pass(callback)) { return; } @@ -429,7 +458,7 @@ let needSetup = false; socket.on("prepare2FA", async (currentPassword, callback) => { try { - if (! await twoFaRateLimiter.pass(callback)) { + if (!await twoFaRateLimiter.pass(callback)) { return; } @@ -478,7 +507,7 @@ let needSetup = false; const clientIP = await server.getClientIP(socket); try { - if (! await twoFaRateLimiter.pass(callback)) { + if (!await twoFaRateLimiter.pass(callback)) { return; } @@ -510,7 +539,7 @@ let needSetup = false; const clientIP = await server.getClientIP(socket); try { - if (! await twoFaRateLimiter.pass(callback)) { + if (!await twoFaRateLimiter.pass(callback)) { return; } @@ -604,7 +633,7 @@ let needSetup = false; throw new Error("Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length."); } - if ((await R.count("user")) !== 0) { + if ((await R.knex("user").count("id as count").first()).count !== 0) { throw new Error("Uptime Kuma has been initialized. If you want to run setup again, please delete the database."); } @@ -741,6 +770,11 @@ let needSetup = false; bean.game = monitor.game; bean.maxretries = monitor.maxretries; bean.port = parseInt(monitor.port); + + if (isNaN(bean.port)) { + bean.port = null; + } + bean.keyword = monitor.keyword; bean.invertKeyword = monitor.invertKeyword; bean.ignoreTls = monitor.ignoreTls; @@ -867,14 +901,17 @@ let needSetup = false; throw new Error("Invalid period."); } + const sqlHourOffset = Database.sqlHourOffset(); + let list = await R.getAll(` - SELECT * FROM heartbeat - WHERE monitor_id = ? AND - time > DATETIME('now', '-' || ? || ' hours') + SELECT * + FROM heartbeat + WHERE monitor_id = ? + AND time > ${sqlHourOffset} ORDER BY time ASC `, [ monitorID, - period, + -period, ]); callback({ @@ -1133,7 +1170,7 @@ let needSetup = false; try { checkLogin(socket); - if (! password.newPassword) { + if (!password.newPassword) { throw new Error("Invalid new password"); } @@ -1462,7 +1499,7 @@ let needSetup = false; ]); let tagId; - if (! tag) { + if (!tag) { // -> If it doesn't exist, create new tag from backup file let beanTag = R.dispense("tag"); beanTag.name = oldTag.name; @@ -1629,10 +1666,6 @@ let needSetup = false; startMonitors(); checkVersion.startInterval(); - if (testMode) { - startUnitTest(); - } - if (e2eTestMode) { startE2eTests(); } @@ -1669,8 +1702,8 @@ async function updateMonitorNotification(monitorID, notificationIDList) { /** * Check if a given user owns a specific monitor - * @param {number} userID - * @param {number} monitorID + * @param {number} userID ID of user to check + * @param {number} monitorID ID of monitor to check * @returns {Promise} * @throws {Error} The specified user does not own the monitor */ @@ -1689,7 +1722,7 @@ async function checkOwner(userID, monitorID) { * Function called after user login * This function is used to send the heartbeat list of a monitor. * @param {Socket} socket Socket.io instance - * @param {Object} user User object + * @param {object} user User object * @returns {Promise} */ async function afterLogin(socket, user) { @@ -1730,16 +1763,11 @@ async function afterLogin(socket, user) { /** * Initialize the database - * @param {boolean} [testMode=false] Should the connection be + * @param {boolean} testMode Should the connection be * started in test mode? * @returns {Promise} */ async function initDatabase(testMode = false) { - if (! fs.existsSync(Database.path)) { - log.info("server", "Copying Database"); - fs.copyFileSync(Database.templatePath, Database.path); - } - log.info("server", "Connecting to the Database"); await Database.connect(testMode); log.info("server", "Connected"); @@ -1760,7 +1788,7 @@ async function initDatabase(testMode = false) { } // If there is no record in user table, it is a new Uptime Kuma instance, need to setup - if ((await R.count("user")) === 0) { + if ((await R.knex("user").count("id as count").first()).count === 0) { log.info("server", "No user, need setup"); needSetup = true; } @@ -1827,7 +1855,10 @@ async function pauseMonitor(userID, monitorID) { } } -/** Resume active monitors */ +/** + * Resume active monitors + * @returns {Promise} + */ async function startMonitors() { let list = await R.find("monitor", " active = 1 "); @@ -1862,12 +1893,19 @@ async function shutdownFunction(signal) { await sleep(2000); await Database.close(); + if (EmbeddedMariaDB.hasInstance()) { + EmbeddedMariaDB.getInstance().stop(); + } + stopBackgroundJobs(); await cloudflaredStop(); Settings.stopCacheCleaner(); } -/** Final function called before application exits */ +/** + * Final function called before application exits + * @returns {void} + */ function finalFunction() { log.info("server", "Graceful shutdown successful!"); } diff --git a/server/settings.js b/server/settings.js index 285b626b9..4776c554d 100644 --- a/server/settings.js +++ b/server/settings.js @@ -96,7 +96,7 @@ class Settings { /** * Get settings based on type * @param {string} type The type of setting - * @returns {Promise} + * @returns {Promise} Settings */ static async getSettings(type) { let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [ @@ -119,7 +119,7 @@ class Settings { /** * Set settings based on type * @param {string} type Type of settings to set - * @param {Object} data Values of settings + * @param {object} data Values of settings * @returns {Promise} */ static async setSettings(type, data) { @@ -150,8 +150,9 @@ class Settings { } /** - * - * @param {string[]} keyList + * Delete selected keys from settings cache + * @param {string[]} keyList Keys to remove + * @returns {void} */ static deleteCache(keyList) { for (let key of keyList) { @@ -159,6 +160,10 @@ class Settings { } } + /** + * Stop the cache cleaner if running + * @returns {void} + */ static stopCacheCleaner() { if (Settings.cacheCleaner) { clearInterval(Settings.cacheCleaner); diff --git a/server/setup-database.js b/server/setup-database.js new file mode 100644 index 000000000..43f5c3c4e --- /dev/null +++ b/server/setup-database.js @@ -0,0 +1,254 @@ +const express = require("express"); +const { log } = require("../src/util"); +const expressStaticGzip = require("express-static-gzip"); +const fs = require("fs"); +const path = require("path"); +const Database = require("./database"); +const { allowDevAllOrigin } = require("./util-server"); +const mysql = require("mysql2/promise"); + +/** + * A standalone express app that is used to setup database + * It is used when db-config.json and kuma.db are not found or invalid + * Once it is configured, it will shutdown and start the main server + */ +class SetupDatabase { + + /** + * Show Setup Page + * @type {boolean} + */ + needSetup = true; + runningSetup = false; + + server; + + /** + * @param args + * @param server + */ + constructor(args, server) { + this.server = server; + + // Priority: env > db-config.json + // If env is provided, write it to db-config.json + // If db-config.json is found, check if it is valid + // If db-config.json is not found or invalid, check if kuma.db is found + // If kuma.db is not found, show setup page + + let dbConfig; + + try { + dbConfig = Database.readDBConfig(); + log.info("setup-database", "db-config.json is found and is valid"); + this.needSetup = false; + + } catch (e) { + log.info("setup-database", "db-config.json is not found or invalid: " + e.message); + + // Check if kuma.db is found (1.X.X users) + if (fs.existsSync(path.join(Database.dataDir, "kuma.db"))) { + this.needSetup = false; + } else { + this.needSetup = true; + } + dbConfig = {}; + } + + if (process.env.UPTIME_KUMA_DB_TYPE) { + this.needSetup = false; + log.info("setup-database", "UPTIME_KUMA_DB_TYPE is provided by env, try to override db-config.json"); + dbConfig.type = process.env.UPTIME_KUMA_DB_TYPE; + dbConfig.hostname = process.env.UPTIME_KUMA_DB_HOSTNAME; + dbConfig.port = process.env.UPTIME_KUMA_DB_PORT; + dbConfig.database = process.env.UPTIME_KUMA_DB_NAME; + dbConfig.username = process.env.UPTIME_KUMA_DB_USERNAME; + dbConfig.password = process.env.UPTIME_KUMA_DB_PASSWORD; + Database.writeDBConfig(dbConfig); + } + + } + + /** + * Show Setup Page + */ + isNeedSetup() { + return this.needSetup; + } + + /** + * + */ + isEnabledEmbeddedMariaDB() { + return process.env.UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB === "1"; + } + + /** + * @param hostname + * @param port + */ + start(hostname, port) { + return new Promise((resolve) => { + const app = express(); + let tempServer; + app.use(express.json()); + + // Disable Keep Alive, otherwise the server will not shutdown, as the client will keep the connection alive + app.use(function (req, res, next) { + res.setHeader("Connection", "close"); + next(); + }); + + app.get("/", async (request, response) => { + response.redirect("/setup-database"); + }); + + app.get("/api/entry-page", async (request, response) => { + allowDevAllOrigin(response); + response.json({ + type: "setup-database", + }); + }); + + app.get("/setup-database-info", (request, response) => { + allowDevAllOrigin(response); + console.log("Request /setup-database-info"); + response.json({ + runningSetup: this.runningSetup, + needSetup: this.needSetup, + isEnabledEmbeddedMariaDB: this.isEnabledEmbeddedMariaDB(), + }); + }); + + app.post("/setup-database", async (request, response) => { + allowDevAllOrigin(response); + + if (this.runningSetup) { + response.status(400).json("Setup is already running"); + return; + } + + this.runningSetup = true; + + let dbConfig = request.body.dbConfig; + + let supportedDBTypes = [ "mariadb", "sqlite" ]; + + if (this.isEnabledEmbeddedMariaDB()) { + supportedDBTypes.push("embedded-mariadb"); + } + + // Validate input + if (typeof dbConfig !== "object") { + response.status(400).json("Invalid dbConfig"); + this.runningSetup = false; + return; + } + + if (!dbConfig.type) { + response.status(400).json("Database Type is required"); + this.runningSetup = false; + return; + } + + if (!supportedDBTypes.includes(dbConfig.type)) { + response.status(400).json("Unsupported Database Type"); + this.runningSetup = false; + return; + } + + // External MariaDB + if (dbConfig.type === "mariadb") { + if (!dbConfig.hostname) { + response.status(400).json("Hostname is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.port) { + response.status(400).json("Port is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.dbName) { + response.status(400).json("Database name is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.username) { + response.status(400).json("Username is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.password) { + response.status(400).json("Password is required"); + this.runningSetup = false; + return; + } + + // Test connection + try { + const connection = await mysql.createConnection({ + host: dbConfig.hostname, + port: dbConfig.port, + user: dbConfig.username, + password: dbConfig.password, + }); + await connection.execute("SELECT 1"); + connection.end(); + } catch (e) { + response.status(400).json("Cannot connect to the database: " + e.message); + this.runningSetup = false; + return; + } + } + + // Write db-config.json + Database.writeDBConfig(dbConfig); + + response.json({ + ok: true, + }); + + // Shutdown down this express and start the main server + log.info("setup-database", "Database is configured, close the setup-database server and start the main server now."); + if (tempServer) { + tempServer.close(() => { + log.info("setup-database", "The setup-database server is closed"); + resolve(); + }); + } else { + resolve(); + } + + }); + + app.use("/", expressStaticGzip("dist", { + enableBrotli: true, + })); + + app.get("*", async (_request, response) => { + response.send(this.server.indexHTML); + }); + + app.options("*", async (_request, response) => { + allowDevAllOrigin(response); + response.end(); + }); + + tempServer = app.listen(port, hostname, () => { + log.info("setup-database", `Starting Setup Database on ${port}`); + let domain = (hostname) ? hostname : "localhost"; + log.info("setup-database", `Open http://${domain}:${port} in your browser`); + log.info("setup-database", "Waiting for user action..."); + }); + }); + } +} + +module.exports = { + SetupDatabase, +}; diff --git a/server/socket-handlers/api-key-socket-handler.js b/server/socket-handlers/api-key-socket-handler.js index 69b0b60de..8a4f5d440 100644 --- a/server/socket-handlers/api-key-socket-handler.js +++ b/server/socket-handlers/api-key-socket-handler.js @@ -9,8 +9,9 @@ const { Settings } = require("../settings"); const { sendAPIKeyList } = require("../client"); /** - * Handlers for Maintenance + * Handlers for API keys * @param {Socket} socket Socket.io instance + * @returns {void} */ module.exports.apiKeySocketHandler = (socket) => { // Add a new api key diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js index ee58e1ad0..809191fe8 100644 --- a/server/socket-handlers/cloudflared-socket-handler.js +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -11,6 +11,7 @@ const cloudflared = new CloudflaredTunnel(); * Change running state * @param {string} running Is it running? * @param {string} message Message to pass + * @returns {void} */ cloudflared.change = (running, message) => { io.to("cloudflared").emit(prefix + "running", running); @@ -19,7 +20,8 @@ cloudflared.change = (running, message) => { /** * Emit an error message - * @param {string} errorMessage + * @param {string} errorMessage Error message to send + * @returns {void} */ cloudflared.error = (errorMessage) => { io.to("cloudflared").emit(prefix + "errorMessage", errorMessage); @@ -28,6 +30,7 @@ cloudflared.error = (errorMessage) => { /** * Handler for cloudflared * @param {Socket} socket Socket.io instance + * @returns {void} */ module.exports.cloudflaredSocketHandler = (socket) => { @@ -89,6 +92,7 @@ module.exports.cloudflaredSocketHandler = (socket) => { /** * Automatically start cloudflared * @param {string} token Cloudflared tunnel token + * @returns {Promise} */ module.exports.autoStart = async (token) => { if (!token) { @@ -106,7 +110,10 @@ module.exports.autoStart = async (token) => { } }; -/** Stop cloudflared */ +/** + * Stop cloudflared + * @returns {Promise} + */ module.exports.stop = async () => { log.info("cloudflared", "Stop cloudflared"); if (cloudflared) { diff --git a/server/socket-handlers/database-socket-handler.js b/server/socket-handlers/database-socket-handler.js index 041cbba06..8441520aa 100644 --- a/server/socket-handlers/database-socket-handler.js +++ b/server/socket-handlers/database-socket-handler.js @@ -4,6 +4,7 @@ const Database = require("../database"); /** * Handlers for database * @param {Socket} socket Socket.io instance + * @returns {void} */ module.exports = (socket) => { diff --git a/server/socket-handlers/docker-socket-handler.js b/server/socket-handlers/docker-socket-handler.js index 542f18cef..d7d768f00 100644 --- a/server/socket-handlers/docker-socket-handler.js +++ b/server/socket-handlers/docker-socket-handler.js @@ -6,6 +6,7 @@ const { log } = require("../../src/util"); /** * Handlers for docker hosts * @param {Socket} socket Socket.io instance + * @returns {void} */ module.exports.dockerSocketHandler = (socket) => { socket.on("addDockerHost", async (dockerHost, dockerHostID, callback) => { diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js index 2f0c63b41..64ace4021 100644 --- a/server/socket-handlers/general-socket-handler.js +++ b/server/socket-handlers/general-socket-handler.js @@ -10,7 +10,7 @@ let gameList = null; /** * Get a game list via GameDig - * @returns {Object[]} list of games supported by GameDig + * @returns {object[]} list of games supported by GameDig */ function getGameList() { if (gameList == null) { diff --git a/server/socket-handlers/maintenance-socket-handler.js b/server/socket-handlers/maintenance-socket-handler.js index ff5bb0fcf..348c0a3cc 100644 --- a/server/socket-handlers/maintenance-socket-handler.js +++ b/server/socket-handlers/maintenance-socket-handler.js @@ -9,6 +9,7 @@ const server = UptimeKumaServer.getInstance(); /** * Handlers for Maintenance * @param {Socket} socket Socket.io instance + * @returns {void} */ module.exports.maintenanceSocketHandler = (socket) => { // Add a new maintenance diff --git a/server/socket-handlers/proxy-socket-handler.js b/server/socket-handlers/proxy-socket-handler.js index e67a829ff..a5f2f4787 100644 --- a/server/socket-handlers/proxy-socket-handler.js +++ b/server/socket-handlers/proxy-socket-handler.js @@ -7,6 +7,7 @@ const server = UptimeKumaServer.getInstance(); /** * Handlers for proxy * @param {Socket} socket Socket.io instance + * @returns {void} */ module.exports.proxySocketHandler = (socket) => { socket.on("addProxy", async (proxy, proxyID, callback) => { diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index eba40daec..427f52d0c 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -11,6 +11,7 @@ const { UptimeKumaServer } = require("../uptime-kuma-server"); /** * Socket handlers for status page * @param {Socket} socket Socket.io instance to add listeners on + * @returns {void} */ module.exports.statusPageSocketHandler = (socket) => { @@ -350,6 +351,8 @@ module.exports.statusPageSocketHandler = (socket) => { * Check slug a-z, 0-9, - only * Regex from: https://stackoverflow.com/questions/22454258/js-regex-string-validation-for-slug * @param {string} slug Slug to test + * @returns {void} + * @throws Slug is not valid */ function checkSlug(slug) { if (typeof slug !== "string") { diff --git a/server/uptime-cache-list.js b/server/uptime-cache-list.js deleted file mode 100644 index d88a9cbf8..000000000 --- a/server/uptime-cache-list.js +++ /dev/null @@ -1,49 +0,0 @@ -const { log } = require("../src/util"); -class UptimeCacheList { - /** - * list[monitorID][duration] - */ - static list = {}; - - /** - * Get the uptime for a specific period - * @param {number} monitorID - * @param {number} duration - * @return {number} - */ - static getUptime(monitorID, duration) { - if (UptimeCacheList.list[monitorID] && UptimeCacheList.list[monitorID][duration]) { - log.debug("UptimeCacheList", "getUptime: " + monitorID + " " + duration); - return UptimeCacheList.list[monitorID][duration]; - } else { - return null; - } - } - - /** - * Add uptime for specified monitor - * @param {number} monitorID - * @param {number} duration - * @param {number} uptime Uptime to add - */ - static addUptime(monitorID, duration, uptime) { - log.debug("UptimeCacheList", "addUptime: " + monitorID + " " + duration); - if (!UptimeCacheList.list[monitorID]) { - UptimeCacheList.list[monitorID] = {}; - } - UptimeCacheList.list[monitorID][duration] = uptime; - } - - /** - * Clear cache for specified monitor - * @param {number} monitorID - */ - static clearCache(monitorID) { - log.debug("UptimeCacheList", "clearCache: " + monitorID); - delete UptimeCacheList.list[monitorID]; - } -} - -module.exports = { - UptimeCacheList, -}; diff --git a/server/uptime-calculator.js b/server/uptime-calculator.js new file mode 100644 index 000000000..2b7f8b2e9 --- /dev/null +++ b/server/uptime-calculator.js @@ -0,0 +1,483 @@ +const dayjs = require("dayjs"); +const { UP, MAINTENANCE, DOWN, PENDING } = require("../src/util"); +const { LimitQueue } = require("./utils/limit-queue"); +const { log } = require("../src/util"); +const { R } = require("redbean-node"); + +/** + * Calculates the uptime of a monitor. + */ +class UptimeCalculator { + + static list = {}; + + /** + * For testing purposes, we can set the current date to a specific date. + * @type {dayjs.Dayjs} + */ + static currentDate = null; + + monitorID; + + /** + * Recent 24-hour uptime, each item is a 1-minute interval + * Key: {number} DivisionKey + */ + minutelyUptimeDataList = new LimitQueue(24 * 60); + + /** + * Daily uptime data, + * Key: {number} DailyKey + */ + dailyUptimeDataList = new LimitQueue(365); + + lastDailyUptimeData = null; + lastUptimeData = null; + + lastDailyStatBean = null; + lastMinutelyStatBean = null; + + /** + * @param monitorID + * @returns {Promise} + */ + static async getUptimeCalculator(monitorID) { + if (!UptimeCalculator.list[monitorID]) { + UptimeCalculator.list[monitorID] = new UptimeCalculator(); + await UptimeCalculator.list[monitorID].init(monitorID); + } + return UptimeCalculator.list[monitorID]; + } + + /** + * @param monitorID + */ + static async remove(monitorID) { + delete UptimeCalculator.list[monitorID]; + } + + /** + * + */ + constructor() { + if (process.env.TEST_BACKEND) { + // Override the getCurrentDate() method to return a specific date + // Only for testing + this.getCurrentDate = () => { + if (UptimeCalculator.currentDate) { + return UptimeCalculator.currentDate; + } else { + return dayjs.utc(); + } + }; + } + } + + /** + * @param {number} monitorID + */ + async init(monitorID) { + this.monitorID = monitorID; + + let now = this.getCurrentDate(); + + // Load minutely data from database (recent 24 hours only) + let minutelyStatBeans = await R.find("stat_minutely", " monitor_id = ? AND timestamp > ? ORDER BY timestamp", [ + monitorID, + this.getMinutelyKey(now.subtract(24, "hour")), + ]); + + for (let bean of minutelyStatBeans) { + let key = bean.timestamp; + this.minutelyUptimeDataList.push(key, { + up: bean.up, + down: bean.down, + avgPing: bean.ping, + }); + } + + // Load daily data from database (recent 365 days only) + let dailyStatBeans = await R.find("stat_daily", " monitor_id = ? AND timestamp > ? ORDER BY timestamp", [ + monitorID, + this.getDailyKey(now.subtract(365, "day").unix()), + ]); + + for (let bean of dailyStatBeans) { + let key = bean.timestamp; + this.dailyUptimeDataList.push(key, { + up: bean.up, + down: bean.down, + avgPing: bean.ping, + }); + } + } + + /** + * @param {number} status status + * @param {number} ping Ping + * @returns {dayjs.Dayjs} date + * @throws {Error} Invalid status + */ + async update(status, ping = 0) { + let date = this.getCurrentDate(); + + // Don't count MAINTENANCE into uptime + if (status === MAINTENANCE) { + return date; + } + + let flatStatus = this.flatStatus(status); + + if (flatStatus === DOWN && ping > 0) { + log.warn("uptime-calc", "The ping is not effective when the status is DOWN"); + } + + let divisionKey = this.getMinutelyKey(date); + let dailyKey = this.getDailyKey(divisionKey); + + let minutelyData = this.minutelyUptimeDataList[divisionKey]; + let dailyData = this.dailyUptimeDataList[dailyKey]; + + if (flatStatus === UP) { + minutelyData.up += 1; + dailyData.up += 1; + + // Only UP status can update the ping + if (!isNaN(ping)) { + // Add avg ping + // The first beat of the minute, the ping is the current ping + if (minutelyData.up === 1) { + minutelyData.avgPing = ping; + } else { + minutelyData.avgPing = (minutelyData.avgPing * (minutelyData.up - 1) + ping) / minutelyData.up; + } + + // Add avg ping (daily) + // The first beat of the day, the ping is the current ping + if (minutelyData.up === 1) { + dailyData.avgPing = ping; + } else { + dailyData.avgPing = (dailyData.avgPing * (dailyData.up - 1) + ping) / dailyData.up; + } + } + + } else { + minutelyData.down += 1; + dailyData.down += 1; + } + + if (dailyData !== this.lastDailyUptimeData) { + this.lastDailyUptimeData = dailyData; + } + + if (minutelyData !== this.lastUptimeData) { + this.lastUptimeData = minutelyData; + } + + // Don't store data in test mode + if (process.env.TEST_BACKEND) { + log.debug("uptime-calc", "Skip storing data in test mode"); + return date; + } + + let dailyStatBean = await this.getDailyStatBean(dailyKey); + dailyStatBean.up = dailyData.up; + dailyStatBean.down = dailyData.down; + dailyStatBean.ping = dailyData.ping; + await R.store(dailyStatBean); + + let minutelyStatBean = await this.getMinutelyStatBean(divisionKey); + minutelyStatBean.up = minutelyData.up; + minutelyStatBean.down = minutelyData.down; + minutelyStatBean.ping = minutelyData.ping; + await R.store(minutelyStatBean); + + // Remove the old data + log.debug("uptime-calc", "Remove old data"); + await R.exec("DELETE FROM stat_minutely WHERE monitor_id = ? AND timestamp < ?", [ + this.monitorID, + this.getMinutelyKey(date.subtract(24, "hour")), + ]); + + return date; + } + + /** + * Get the daily stat bean + * @param {number} timestamp milliseconds + * @returns {Promise} stat_daily bean + */ + async getDailyStatBean(timestamp) { + if (this.lastDailyStatBean && this.lastDailyStatBean.timestamp === timestamp) { + return this.lastDailyStatBean; + } + + let bean = await R.findOne("stat_daily", " monitor_id = ? AND timestamp = ?", [ + this.monitorID, + timestamp, + ]); + + if (!bean) { + bean = R.dispense("stat_daily"); + bean.monitor_id = this.monitorID; + bean.timestamp = timestamp; + } + + this.lastDailyStatBean = bean; + return this.lastDailyStatBean; + } + + /** + * Get the minutely stat bean + * @param {number} timestamp milliseconds + * @returns {Promise} stat_minutely bean + */ + async getMinutelyStatBean(timestamp) { + if (this.lastMinutelyStatBean && this.lastMinutelyStatBean.timestamp === timestamp) { + return this.lastMinutelyStatBean; + } + + let bean = await R.findOne("stat_minutely", " monitor_id = ? AND timestamp = ?", [ + this.monitorID, + timestamp, + ]); + + if (!bean) { + bean = R.dispense("stat_minutely"); + bean.monitor_id = this.monitorID; + bean.timestamp = timestamp; + } + + this.lastMinutelyStatBean = bean; + return this.lastMinutelyStatBean; + } + + /** + * @param {dayjs.Dayjs} date The heartbeat date + * @returns {number} Timestamp + */ + getMinutelyKey(date) { + // Convert the current date to the nearest minute (e.g. 2021-01-01 12:34:56 -> 2021-01-01 12:34:00) + date = date.startOf("minute"); + + // Convert to timestamp in second + let divisionKey = date.unix(); + + if (! (divisionKey in this.minutelyUptimeDataList)) { + let last = this.minutelyUptimeDataList.getLastKey(); + if (last && last > divisionKey) { + log.warn("uptime-calc", "The system time has been changed? The uptime data may be inaccurate."); + } + + this.minutelyUptimeDataList.push(divisionKey, { + up: 0, + down: 0, + avgPing: 0, + }); + } + + return divisionKey; + } + + /** + * Convert timestamp to daily key + * @param {number} timestamp Timestamp + * @returns {number} Timestamp + */ + getDailyKey(timestamp) { + let date = dayjs.unix(timestamp); + + // Convert the date to the nearest day (e.g. 2021-01-01 12:34:56 -> 2021-01-01 00:00:00) + // Considering if the user keep changing could affect the calculation, so use UTC time to avoid this problem. + date = date.utc().startOf("day"); + let dailyKey = date.unix(); + + if (!this.dailyUptimeDataList[dailyKey]) { + let last = this.dailyUptimeDataList.getLastKey(); + if (last && last > dailyKey) { + log.warn("uptime-calc", "The system time has been changed? The uptime data may be inaccurate."); + } + + this.dailyUptimeDataList.push(dailyKey, { + up: 0, + down: 0, + avgPing: 0, + }); + } + + return dailyKey; + } + + /** + * Flat status to UP or DOWN + * @param {number} status + * @returns {number} + * @throws {Error} Invalid status + */ + flatStatus(status) { + switch (status) { + case UP: + // case MAINTENANCE: + return UP; + case DOWN: + case PENDING: + return DOWN; + } + throw new Error("Invalid status"); + } + + /** + * @param {number} num + * @param {string} type "day" | "minute" + */ + getData(num, type = "day") { + let key; + + if (type === "day") { + key = this.getDailyKey(this.getCurrentDate().unix()); + } else { + if (num > 24 * 60) { + throw new Error("The maximum number of minutes is 1440"); + } + key = this.getMinutelyKey(this.getCurrentDate()); + } + + let total = { + up: 0, + down: 0, + }; + + let totalPing = 0; + let endTimestamp; + + if (type === "day") { + endTimestamp = key - 86400 * (num - 1); + } else { + endTimestamp = key - 60 * (num - 1); + } + + // Sum up all data in the specified time range + while (key >= endTimestamp) { + let data; + + if (type === "day") { + data = this.dailyUptimeDataList[key]; + } else { + data = this.minutelyUptimeDataList[key]; + } + + if (data) { + total.up += data.up; + total.down += data.down; + totalPing += data.avgPing * data.up; + } + + // Previous day + if (type === "day") { + key -= 86400; + } else { + key -= 60; + } + } + + let uptimeData = new UptimeDataResult(); + + if (total.up === 0 && total.down === 0) { + if (type === "day" && this.lastDailyUptimeData) { + total = this.lastDailyUptimeData; + totalPing = total.avgPing * total.up; + } else if (type === "minute" && this.lastUptimeData) { + total = this.lastUptimeData; + totalPing = total.avgPing * total.up; + } else { + uptimeData.uptime = 0; + uptimeData.avgPing = null; + return uptimeData; + } + } + + let avgPing; + + if (total.up === 0) { + avgPing = null; + } else { + avgPing = totalPing / total.up; + } + + uptimeData.uptime = total.up / (total.up + total.down); + uptimeData.avgPing = avgPing; + return uptimeData; + } + + /** + * Get the uptime data by duration + * @param {'24h'|'30d'|'1y'} duration Only accept 24h, 30d, 1y + * @returns {UptimeDataResult} UptimeDataResult + * @throws {Error} Invalid duration + */ + getDataByDuration(duration) { + if (duration === "24h") { + return this.get24Hour(); + } else if (duration === "30d") { + return this.get30Day(); + } else if (duration === "1y") { + return this.get1Year(); + } else { + throw new Error("Invalid duration"); + } + } + + /** + * 1440 = 24 * 60mins + * @returns {UptimeDataResult} UptimeDataResult + */ + get24Hour() { + return this.getData(1440, "minute"); + } + + /** + * @returns {UptimeDataResult} UptimeDataResult + */ + get7Day() { + return this.getData(7); + } + + /** + * @returns {UptimeDataResult} UptimeDataResult + */ + get30Day() { + return this.getData(30); + } + + /** + * @returns {UptimeDataResult} UptimeDataResult + */ + get1Year() { + return this.getData(365); + } + + /** + * @returns {dayjs.Dayjs} Current date + */ + getCurrentDate() { + return dayjs.utc(); + } + +} + +class UptimeDataResult { + /** + * @type {number} Uptime + */ + uptime; + + /** + * @type {number} Average ping + */ + avgPing; +} + +module.exports = { + UptimeCalculator, + UptimeDataResult, +}; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 6acc8d4df..ff34e8ac7 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -20,7 +20,7 @@ const path = require("path"); */ class UptimeKumaServer { /** - * + * Current server instance * @type {UptimeKumaServer} */ static instance = null; @@ -49,7 +49,6 @@ class UptimeKumaServer { indexHTML = ""; /** - * * @type {{}} */ static monitorTypeList = { @@ -62,6 +61,12 @@ class UptimeKumaServer { */ jwtSecret = null; + /** + * Get the current instance of the server if it exists, otherwise + * create a new instance. + * @param {object} args Arguments to pass to instance constructor + * @returns {UptimeKumaServer} Server instance + */ static getInstance(args) { if (UptimeKumaServer.instance == null) { UptimeKumaServer.instance = new UptimeKumaServer(args); @@ -69,6 +74,9 @@ class UptimeKumaServer { return UptimeKumaServer.instance; } + /** + * @param {object} args Arguments to initialise server with + */ constructor(args) { // SSL const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined; @@ -106,7 +114,10 @@ class UptimeKumaServer { this.io = new Server(this.httpServer); } - /** Initialise app after the database has been set up */ + /** + * Initialise app after the database has been set up + * @returns {Promise} + */ async initAfterDatabaseReady() { // Static this.app.use("/screenshots", express.static(Database.screenshotDir)); @@ -123,8 +134,8 @@ class UptimeKumaServer { /** * Send list of monitors to client - * @param {Socket} socket - * @returns {Object} List of monitors + * @param {Socket} socket Socket to send list on + * @returns {object} List of monitors */ async sendMonitorList(socket) { let list = await this.getMonitorJSONList(socket.userID); @@ -135,7 +146,7 @@ class UptimeKumaServer { /** * Get a list of monitors for the given user. * @param {string} userID - The ID of the user to get monitors for. - * @returns {Promise} A promise that resolves to an object with monitor IDs as keys and monitor objects as values. + * @returns {Promise} A promise that resolves to an object with monitor IDs as keys and monitor objects as values. * * Generated by Trelent */ @@ -156,7 +167,7 @@ class UptimeKumaServer { /** * Send maintenance list to client * @param {Socket} socket Socket.io instance to send to - * @returns {Object} + * @returns {object} Maintenance list */ async sendMaintenanceList(socket) { return await this.sendMaintenanceListByUserID(socket.userID); @@ -164,8 +175,8 @@ class UptimeKumaServer { /** * Send list of maintenances to user - * @param {number} userID - * @returns {Object} + * @param {number} userID User to send list to + * @returns {object} Maintenance list */ async sendMaintenanceListByUserID(userID) { let list = await this.getMaintenanceJSONList(userID); @@ -176,7 +187,7 @@ class UptimeKumaServer { /** * Get a list of maintenances for the given user. * @param {string} userID - The ID of the user to get maintenances for. - * @returns {Promise} A promise that resolves to an object with maintenance IDs as keys and maintenances objects as values. + * @returns {Promise} A promise that resolves to an object with maintenance IDs as keys and maintenances objects as values. */ async getMaintenanceJSONList(userID) { let result = {}; @@ -188,7 +199,7 @@ class UptimeKumaServer { /** * Load maintenance list and run - * @param userID + * @param {any} userID Unused * @returns {Promise} */ async loadMaintenanceList(userID) { @@ -202,6 +213,11 @@ class UptimeKumaServer { } } + /** + * Retrieve a specific maintenance + * @param {number} maintenanceID ID of maintenance to retrieve + * @returns {(object|null)} Maintenance if it exists + */ getMaintenance(maintenanceID) { if (this.maintenanceList[maintenanceID]) { return this.maintenanceList[maintenanceID]; @@ -213,6 +229,7 @@ class UptimeKumaServer { * Write error to log file * @param {any} error The error to write * @param {boolean} outputToConsole Should the error also be output to console? + * @returns {void} */ static errorLog(error, outputToConsole = true) { const errorLogStream = fs.createWriteStream(path.join(Database.dataDir, "/error.log"), { @@ -237,8 +254,8 @@ class UptimeKumaServer { /** * Get the IP of the client connected to the socket - * @param {Socket} socket - * @returns {string} + * @param {Socket} socket Socket to query + * @returns {string} IP of client */ async getClientIP(socket) { let clientIP = socket.client.conn.remoteAddress; @@ -262,7 +279,7 @@ class UptimeKumaServer { * Attempt to get the current server timezone * If this fails, fall back to environment variables and then make a * guess. - * @returns {Promise} + * @returns {Promise} Current timezone */ async getTimezone() { // From process.env.TZ @@ -307,7 +324,7 @@ class UptimeKumaServer { /** * Get the current offset - * @returns {string} + * @returns {string} Time offset */ getTimezoneOffset() { return dayjs().format("Z"); @@ -315,7 +332,9 @@ class UptimeKumaServer { /** * Throw an error if the timezone is invalid - * @param timezone + * @param {string} timezone Timezone to test + * @returns {void} + * @throws The timezone is invalid */ checkTimezone(timezone) { try { @@ -327,7 +346,8 @@ class UptimeKumaServer { /** * Set the current server timezone and environment variables - * @param {string} timezone + * @param {string} timezone Timezone to set + * @returns {Promise} */ async setTimezone(timezone) { this.checkTimezone(timezone); @@ -363,6 +383,7 @@ class UptimeKumaServer { /** * Start all system services (e.g. nscd) * For now, only used in Docker + * @returns {void} */ startNSCDServices() { if (process.env.UPTIME_KUMA_IS_CONTAINER) { @@ -377,6 +398,7 @@ class UptimeKumaServer { /** * Stop all system services + * @returns {void} */ stopNSCDServices() { if (process.env.UPTIME_KUMA_IS_CONTAINER) { diff --git a/server/util-server.js b/server/util-server.js index 8354b5609..1540f6895 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -29,6 +29,9 @@ const { }, } = require("node-radius-utils"); const dayjs = require("dayjs"); +const readline = require("readline"); +const rl = readline.createInterface({ input: process.stdin, + output: process.stdout }); // SASLOptions used in JSDoc // eslint-disable-next-line no-unused-vars @@ -37,7 +40,7 @@ const { Kafka, SASLOptions } = require("kafkajs"); const isWindows = process.platform === /^win/.test(process.platform); /** * Init or reset JWT secret - * @returns {Promise} + * @returns {Promise} JWT secret */ exports.initJWTSecret = async () => { let jwtSecretBean = await R.findOne("setting", " `key` = ? ", [ @@ -57,7 +60,7 @@ exports.initJWTSecret = async () => { /** * Decodes a jwt and returns the payload portion without verifying the jqt. * @param {string} jwt The input jwt as a string - * @returns {Object} Decoded jwt payload object + * @returns {object} Decoded jwt payload object */ exports.decodeJwt = (jwt) => { return JSON.parse(Buffer.from(jwt.split(".")[1], "base64").toString()); @@ -121,7 +124,7 @@ exports.tcping = function (hostname, port) { /** * Ping the specified machine * @param {string} hostname Hostname / address of machine - * @param {number} [size=56] Size of packet to send + * @param {number} size Size of packet to send * @returns {Promise} Time for ping in ms rounded to nearest integer */ exports.ping = async (hostname, size = 56) => { @@ -144,7 +147,7 @@ exports.ping = async (hostname, size = 56) => { * Ping the specified machine * @param {string} hostname Hostname / address of machine to ping * @param {boolean} ipv6 Should IPv6 be used? - * @param {number} [size = 56] Size of ping packet to send + * @param {number} size Size of ping packet to send * @returns {Promise} Time for ping in ms rounded to nearest integer */ exports.pingAsync = function (hostname, ipv6 = false, size = 56) { @@ -176,9 +179,9 @@ exports.pingAsync = function (hostname, ipv6 = false, size = 56) { * @param {string} hostname Hostname / address of machine to test * @param {string} topic MQTT topic * @param {string} okMessage Expected result - * @param {Object} [options={}] MQTT options. Contains port, username, + * @param {object} options MQTT options. Contains port, username, * password and interval (interval defaults to 20) - * @returns {Promise} + * @returns {Promise} Received MQTT message */ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) { return new Promise((resolve, reject) => { @@ -240,16 +243,17 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) { /** * Monitor Kafka using Producer + * @param {string[]} brokers List of kafka brokers to connect, host and + * port joined by ':' * @param {string} topic Topic name to produce into * @param {string} message Message to produce - * @param {Object} [options={interval = 20, allowAutoTopicCreation = false, ssl = false, clientId = "Uptime-Kuma"}] - * Kafka client options. Contains ssl, clientId, allowAutoTopicCreation and - * interval (interval defaults to 20, allowAutoTopicCreation defaults to false, clientId defaults to "Uptime-Kuma" - * and ssl defaults to false) - * @param {string[]} brokers List of kafka brokers to connect, host and port joined by ':' - * @param {SASLOptions} [saslOptions={}] Options for kafka client Authentication (SASL) (defaults to - * {}) - * @returns {Promise} + * @param {object} options Kafka client options. Contains ssl, clientId, + * allowAutoTopicCreation and interval (interval defaults to 20, + * allowAutoTopicCreation defaults to false, clientId defaults to + * "Uptime-Kuma" and ssl defaults to false) + * @param {SASLOptions} saslOptions Options for kafka client + * Authentication (SASL) (defaults to {}) + * @returns {Promise} Status message */ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, saslOptions = {}) { return new Promise((resolve, reject) => { @@ -328,9 +332,9 @@ exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, sa /** * Use NTLM Auth for a http request. - * @param {Object} options The http request options - * @param {Object} ntlmOptions The auth options - * @returns {Promise<(string[]|Object[]|Object)>} + * @param {object} options The http request options + * @param {object} ntlmOptions The auth options + * @returns {Promise<(string[] | object[] | object)>} NTLM response */ exports.httpNtlm = function (options, ntlmOptions) { return new Promise((resolve, reject) => { @@ -352,7 +356,7 @@ exports.httpNtlm = function (options, ntlmOptions) { * @param {string} resolverServer The DNS server to use * @param {string} resolverPort Port the DNS server is listening on * @param {string} rrtype The type of record to request - * @returns {Promise<(string[]|Object[]|Object)>} + * @returns {Promise<(string[] | object[] | object)>} DNS response */ exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) { const resolver = new Resolver(); @@ -385,7 +389,8 @@ exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) { * Run a query on SQL Server * @param {string} connectionString The database connection string * @param {string} query The query to validate the database with - * @returns {Promise<(string[]|Object[]|Object)>} + * @returns {Promise<(string[] | object[] | object)>} Response from + * server */ exports.mssqlQuery = async function (connectionString, query) { let pool; @@ -406,7 +411,8 @@ exports.mssqlQuery = async function (connectionString, query) { * Run a query on Postgres * @param {string} connectionString The database connection string * @param {string} query The query to validate the database with - * @returns {Promise<(string[]|Object[]|Object)>} + * @returns {Promise<(string[] | object[] | object)>} Response from + * server */ exports.postgresQuery = function (connectionString, query) { return new Promise((resolve, reject) => { @@ -452,7 +458,7 @@ exports.postgresQuery = function (connectionString, query) { * Run a query on MySQL/MariaDB * @param {string} connectionString The database connection string * @param {string} query The query to validate the database with - * @returns {Promise<(string)>} + * @returns {Promise<(string)>} Response from server */ exports.mysqlQuery = function (connectionString, query) { return new Promise((resolve, reject) => { @@ -483,9 +489,10 @@ exports.mysqlQuery = function (connectionString, query) { }; /** - * Connect to and Ping a MongoDB database + * Connect to and ping a MongoDB database * @param {string} connectionString The database connection string - * @returns {Promise<(string[]|Object[]|Object)>} + * @returns {Promise<(string[] | object[] | object)>} Response from + * server */ exports.mongodbPing = async function (connectionString) { let client = await MongoClient.connect(connectionString); @@ -507,9 +514,9 @@ exports.mongodbPing = async function (connectionString) { * @param {string} calledStationId ID of called station * @param {string} callingStationId ID of calling station * @param {string} secret Secret to use - * @param {number} [port=1812] Port to contact radius server on - * @param {number} [timeout=2500] Timeout for connection to use - * @returns {Promise} + * @param {number} port Port to contact radius server on + * @param {number} timeout Timeout for connection to use + * @returns {Promise} Response from server */ exports.radius = function ( hostname, @@ -549,6 +556,7 @@ exports.radius = function ( /** * Redis server ping * @param {string} dsn The redis connection string + * @returns {Promise} Response from redis server */ exports.redisPingAsync = function (dsn) { return new Promise((resolve, reject) => { @@ -590,7 +598,7 @@ exports.setting = async function (key) { }; /** - * Sets the specified setting to specifed value + * Sets the specified setting to specified value * @param {string} key Key of setting to set * @param {any} value Value to set to * @param {?string} type Type of setting @@ -603,7 +611,7 @@ exports.setSetting = async function (key, value, type = null) { /** * Get settings based on type * @param {string} type The type of setting - * @returns {Promise} + * @returns {Promise} Settings of requested type */ exports.getSettings = async function (type) { return await Settings.getSettings(type); @@ -612,7 +620,7 @@ exports.getSettings = async function (type) { /** * Set settings based on type * @param {string} type Type of settings to set - * @param {Object} data Values of settings + * @param {object} data Values of settings * @returns {Promise} */ exports.setSettings = async function (type, data) { @@ -626,7 +634,7 @@ exports.setSettings = async function (type, data) { * Get number of days between two dates * @param {Date} validFrom Start date * @param {Date} validTo End date - * @returns {number} + * @returns {number} Number of days */ const getDaysBetween = (validFrom, validTo) => Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); @@ -635,7 +643,7 @@ const getDaysBetween = (validFrom, validTo) => * Get days remaining from a time range * @param {Date} validFrom Start date * @param {Date} validTo End date - * @returns {number} + * @returns {number} Number of days remaining */ const getDaysRemaining = (validFrom, validTo) => { const daysRemaining = getDaysBetween(validFrom, validTo); @@ -647,8 +655,9 @@ const getDaysRemaining = (validFrom, validTo) => { /** * Fix certificate info for display - * @param {Object} info The chain obtained from getPeerCertificate() - * @returns {Object} An object representing certificate information + * @param {object} info The chain obtained from getPeerCertificate() + * @returns {object} An object representing certificate information + * @throws The certificate chain length exceeded 500. */ const parseCertificateInfo = function (info) { let link = info; @@ -695,8 +704,9 @@ const parseCertificateInfo = function (info) { /** * Check if certificate is valid - * @param {Object} res Response object from axios - * @returns {Object} Object containing certificate information + * @param {object} res Response object from axios + * @returns {object} Object containing certificate information + * @throws No socket was found to check certificate for */ exports.checkCertificate = function (res) { if (!res.request.res.socket) { @@ -754,7 +764,7 @@ exports.checkStatusCode = function (status, acceptedCodes) { * Get total number of clients in room * @param {Server} io Socket server instance * @param {string} roomName Name of room to check - * @returns {number} + * @returns {number} Total clients in room */ exports.getTotalClientInRoom = (io, roomName) => { @@ -781,7 +791,8 @@ exports.getTotalClientInRoom = (io, roomName) => { /** * Allow CORS all origins if development - * @param {Object} res Response object from axios + * @param {object} res Response object from axios + * @returns {void} */ exports.allowDevAllOrigin = (res) => { if (process.env.NODE_ENV === "development") { @@ -791,16 +802,20 @@ exports.allowDevAllOrigin = (res) => { /** * Allow CORS all origins - * @param {Object} res Response object from axios + * @param {object} res Response object from axios + * @returns {void} */ exports.allowAllOrigin = (res) => { res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); }; /** * Check if a user is logged in * @param {Socket} socket Socket instance + * @returns {void} + * @throws The user is not logged in */ exports.checkLogin = (socket) => { if (!socket.userID) { @@ -811,8 +826,10 @@ exports.checkLogin = (socket) => { /** * For logged-in users, double-check the password * @param {Socket} socket Socket.io instance - * @param {string} currentPassword - * @returns {Promise} + * @param {string} currentPassword Password to validate + * @returns {Promise} User + * @throws The current password is not a string + * @throws The provided password is not correct */ exports.doubleCheckPassword = async (socket, currentPassword) => { if (typeof currentPassword !== "string") { @@ -830,27 +847,10 @@ exports.doubleCheckPassword = async (socket, currentPassword) => { return user; }; -/** Start Unit tests */ -exports.startUnitTest = async () => { - console.log("Starting unit test..."); - const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; - const child = childProcess.spawn(npm, [ "run", "jest-backend" ]); - - child.stdout.on("data", (data) => { - console.log(data.toString()); - }); - - child.stderr.on("data", (data) => { - console.log(data.toString()); - }); - - child.on("close", function (code) { - console.log("Jest exit code: " + code); - process.exit(code); - }); -}; - -/** Start end-to-end tests */ +/** + * Start end-to-end tests + * @returns {void} + */ exports.startE2eTests = async () => { console.log("Starting unit test..."); const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; @@ -873,7 +873,7 @@ exports.startE2eTests = async () => { /** * Convert unknown string to UTF8 * @param {Uint8Array} body Buffer - * @returns {string} + * @returns {string} UTF8 string */ exports.convertToUTF8 = (body) => { const guessEncoding = chardet.detect(body); @@ -885,11 +885,10 @@ exports.convertToUTF8 = (body) => { * Returns a color code in hex format based on a given percentage: * 0% => hue = 10 => red * 100% => hue = 90 => green - * * @param {number} percentage float, 0 to 1 - * @param {number} maxHue - * @param {number} minHue, int - * @returns {string}, hex value + * @param {number} maxHue Maximum hue - int + * @param {number} minHue Minimum hue - int + * @returns {string} Color in hex */ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { const hue = percentage * (maxHue - minHue) + minHue; @@ -902,10 +901,9 @@ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { /** * Joins and array of string to one string after filtering out empty values - * - * @param {string[]} parts - * @param {string} connector - * @returns {string} + * @param {string[]} parts Strings to join + * @param {string} connector Separator for joined strings + * @returns {string} Joined strings */ exports.filterAndJoin = (parts, connector = "") => { return parts.filter((part) => !!part && part !== "").join(connector); @@ -913,8 +911,9 @@ exports.filterAndJoin = (parts, connector = "") => { /** * Send an Error response - * @param {Object} res Express response object - * @param {string} [msg=""] Message to send + * @param {object} res Express response object + * @param {string} msg Message to send + * @returns {void} */ module.exports.sendHttpError = (res, msg = "") => { if (msg.includes("SQLITE_BUSY") || msg.includes("SQLITE_LOCKED")) { @@ -935,6 +934,13 @@ module.exports.sendHttpError = (res, msg = "") => { } }; +/** + * Convert timezone of time object + * @param {object} obj Time object to update + * @param {string} timezone New timezone to set + * @param {boolean} timeObjectToUTC Convert time object to UTC + * @returns {object} Time object with updated timezone + */ function timeObjectConvertTimezone(obj, timezone, timeObjectToUTC = true) { let offsetString; @@ -977,20 +983,20 @@ function timeObjectConvertTimezone(obj, timezone, timeObjectToUTC = true) { } /** - * - * @param {object} obj - * @param {string} timezone - * @returns {object} + * Convert time object to UTC + * @param {object} obj Object to convert + * @param {string} timezone Timezone of time object + * @returns {object} Updated time object */ module.exports.timeObjectToUTC = (obj, timezone = undefined) => { return timeObjectConvertTimezone(obj, timezone, true); }; /** - * - * @param {object} obj - * @param {string} timezone - * @returns {object} + * Convert time object to local time + * @param {object} obj Object to convert + * @param {string} timezone Timezone to convert to + * @returns {object} Updated object */ module.exports.timeObjectToLocal = (obj, timezone = undefined) => { return timeObjectConvertTimezone(obj, timezone, false); @@ -998,7 +1004,8 @@ module.exports.timeObjectToLocal = (obj, timezone = undefined) => { /** * Create gRPC client stib - * @param {Object} options from gRPC client + * @param {object} options from gRPC client + * @returns {Promise} Result of gRPC query */ module.exports.grpcQuery = async (options) => { const { grpcUrl, grpcProtobufData, grpcServiceName, grpcEnableTls, grpcMethod, grpcBody } = options; @@ -1053,6 +1060,8 @@ module.exports.grpcQuery = async (options) => { }); }; +module.exports.prompt = (query) => new Promise((resolve) => rl.question(query, resolve)); + // For unit test, export functions if (process.env.TEST_BACKEND) { module.exports.__test = { diff --git a/server/utils/array-with-key.js b/server/utils/array-with-key.js new file mode 100644 index 000000000..847bc24f7 --- /dev/null +++ b/server/utils/array-with-key.js @@ -0,0 +1,79 @@ +/** + * An object that can be used as an array with a key + * Like PHP's array + */ +class ArrayWithKey { + __stack = []; + + /** + * + */ + constructor() { + + } + + /** + * @param key + * @param value + */ + push(key, value) { + this[key] = value; + this.__stack.push(key); + } + + /** + * + */ + pop() { + let key = this.__stack.pop(); + let prop = this[key]; + delete this[key]; + return prop; + } + + /** + * + */ + getLastKey() { + if (this.__stack.length === 0) { + return null; + } + return this.__stack[this.__stack.length - 1]; + } + + /** + * + */ + shift() { + let key = this.__stack.shift(); + let value = this[key]; + delete this[key]; + return { + key, + value, + }; + } + + /** + * + */ + length() { + return this.__stack.length; + } + + /** + * Get the last element + * @returns {*|null} The last element, or null if the array is empty + */ + last() { + let key = this.getLastKey(); + if (key === null) { + return null; + } + return this[key]; + } +} + +module.exports = { + ArrayWithKey +}; diff --git a/server/utils/limit-queue.js b/server/utils/limit-queue.js new file mode 100644 index 000000000..a4744f4a3 --- /dev/null +++ b/server/utils/limit-queue.js @@ -0,0 +1,37 @@ +const { ArrayWithKey } = require("./array-with-key"); + +/** + * Limit Queue + * The first element will be removed when the length exceeds the limit + */ +class LimitQueue extends ArrayWithKey { + + __limit; + __onExceed = null; + + /** + * @param {number} limit + */ + constructor(limit) { + super(); + this.__limit = limit; + } + + /** + * @inheritDoc + */ + push(key, value) { + super.push(key, value); + if (this.length() > this.__limit) { + let item = this.shift(); + if (this.__onExceed) { + this.__onExceed(item); + } + } + } + +} + +module.exports = { + LimitQueue +}; diff --git a/src/components/APIKeyDialog.vue b/src/components/APIKeyDialog.vue index 4bba69fec..ac9cffe36 100644 --- a/src/components/APIKeyDialog.vue +++ b/src/components/APIKeyDialog.vue @@ -126,6 +126,7 @@ export default { methods: { /** * Show modal + * @returns {void} */ show() { this.id = null; @@ -138,7 +139,10 @@ export default { this.keyaddmodal.show(); }, - /** Submit data to server */ + /** + * Submit data to server + * @returns {Promise} + */ async submit() { this.processing = true; @@ -159,7 +163,10 @@ export default { }); }, - /** Clear Form inputs */ + /** + * Clear Form inputs + * @returns {void} + */ clearForm() { this.key = { name: "", diff --git a/src/components/BadgeGeneratorDialog.vue b/src/components/BadgeGeneratorDialog.vue index aa6fa6e88..5ae0a8771 100644 --- a/src/components/BadgeGeneratorDialog.vue +++ b/src/components/BadgeGeneratorDialog.vue @@ -279,8 +279,9 @@ export default { methods: { /** * Setting monitor - * @param {number} monitorId ID of monitor - * @param {string} monitorName Name of monitor + * @param {number} monitorId ID of monitor + * @param {string} monitorName Name of monitor + * @returns {void} */ show(monitorId, monitorName) { this.monitor = { diff --git a/src/components/CertificateInfoRow.vue b/src/components/CertificateInfoRow.vue index f02d1d7b9..8a4a875c7 100644 --- a/src/components/CertificateInfoRow.vue +++ b/src/components/CertificateInfoRow.vue @@ -65,9 +65,9 @@ export default { methods: { /** * Format the subject of the certificate - * @param {Object} subject Object representing the certificates + * @param {object} subject Object representing the certificates * subject - * @returns {string} + * @returns {string} Certificate subject */ formatSubject(subject) { if (subject.O && subject.CN && subject.C) { diff --git a/src/components/Confirm.vue b/src/components/Confirm.vue index 4bc2217cb..e855b6769 100644 --- a/src/components/Confirm.vue +++ b/src/components/Confirm.vue @@ -58,18 +58,23 @@ export default { this.modal = new Modal(this.$refs.modal); }, methods: { - /** Show the confirm dialog */ + /** + * Show the confirm dialog + * @returns {void} + */ show() { this.modal.show(); }, /** - * @emits string "yes" Notify the parent when Yes is pressed + * @fires string "yes" Notify the parent when Yes is pressed + * @returns {void} */ yes() { this.$emit("yes"); }, /** - * @emits string "no" Notify the parent when No is pressed + * @fires string "no" Notify the parent when No is pressed + * @returns {void} */ no() { this.$emit("no"); diff --git a/src/components/CopyableInput.vue b/src/components/CopyableInput.vue index 943193f4d..951fded94 100644 --- a/src/components/CopyableInput.vue +++ b/src/components/CopyableInput.vue @@ -90,19 +90,25 @@ export default { }, methods: { - /** Show the input */ + /** + * Show the input + * @returns {void} + */ showInput() { this.visibility = "text"; }, - /** Hide the input */ + /** + * Hide the input + * @returns {void} + */ hideInput() { this.visibility = "password"; }, /** * Copy the provided text to the users clipboard - * @param {string} textToCopy + * @param {string} textToCopy Text to copy to clipboard * @returns {Promise} */ copyToClipboard(textToCopy) { diff --git a/src/components/CreateGroupDialog.vue b/src/components/CreateGroupDialog.vue index ed20610c7..ba7fe6eb7 100644 --- a/src/components/CreateGroupDialog.vue +++ b/src/components/CreateGroupDialog.vue @@ -43,10 +43,17 @@ export default { this.modal = new Modal(this.$refs.modal); }, methods: { - /** Show the confirm dialog */ + /** + * Show the confirm dialog + * @returns {void} + */ show() { this.modal.show(); }, + /** + * Dialog confirmed + * @returns {void} + */ confirm() { this.$emit("added", this.groupName); this.modal.hide(); diff --git a/src/components/DockerHostDialog.vue b/src/components/DockerHostDialog.vue index 335e8f2b3..2744264a6 100644 --- a/src/components/DockerHostDialog.vue +++ b/src/components/DockerHostDialog.vue @@ -91,7 +91,10 @@ export default { }, methods: { - /** Confirm deletion of docker host */ + /** + * Confirm deletion of docker host + * @returns {void} + */ deleteConfirm() { this.modal.hide(); this.$refs.confirmDelete.show(); @@ -99,7 +102,8 @@ export default { /** * Show specified docker host - * @param {number} dockerHostID + * @param {number} dockerHostID ID of host to show + * @returns {void} */ show(dockerHostID) { if (dockerHostID) { @@ -131,7 +135,10 @@ export default { this.modal.show(); }, - /** Add docker host */ + /** + * Add docker host + * @returns {void} + */ submit() { this.processing = true; this.$root.getSocket().emit("addDockerHost", this.dockerHost, this.id, (res) => { @@ -150,7 +157,10 @@ export default { }); }, - /** Test the docker host */ + /** + * Test the docker host + * @returns {void} + */ test() { this.processing = true; this.$root.getSocket().emit("testDockerHost", this.dockerHost, (res) => { @@ -159,7 +169,10 @@ export default { }); }, - /** Delete this docker host */ + /** + * Delete this docker host + * @returns {void} + */ deleteDockerHost() { this.processing = true; this.$root.getSocket().emit("deleteDockerHost", this.id, (res) => { diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 8323f7cfe..d7069ffd8 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -56,6 +56,7 @@ export default { /** * If heartbeatList is null, get it from $root.heartbeatList + * @returns {object} Heartbeat list */ beatList() { if (this.heartbeatList === null) { @@ -67,8 +68,7 @@ export default { /** * Calculates the amount of beats of padding needed to fill the length of shortBeatList. - * - * @return {number} The amount of beats of padding needed to fill the length of shortBeatList. + * @returns {number} The amount of beats of padding needed to fill the length of shortBeatList. */ numPadding() { if (!this.beatList) { @@ -148,7 +148,7 @@ export default { /** * Returns the style object for positioning the time element. - * @return {Object} The style object containing the CSS properties for positioning the time element. + * @returns {object} The style object containing the CSS properties for positioning the time element. */ timeStyle() { return { @@ -158,8 +158,7 @@ export default { /** * Calculates the time elapsed since the first valid beat. - * - * @return {string} The time elapsed in minutes or hours. + * @returns {string} The time elapsed in minutes or hours. */ timeSinceFirstBeat() { const firstValidBeat = this.shortBeatList.at(this.numPadding); @@ -173,8 +172,7 @@ export default { /** * Calculates the elapsed time since the last valid beat was registered. - * - * @return {string} The elapsed time in a minutes, hours or "now". + * @returns {string} The elapsed time in a minutes, hours or "now". */ timeSinceLastBeat() { const lastValidBeat = this.shortBeatList.at(-1); @@ -241,7 +239,10 @@ export default { this.resize(); }, methods: { - /** Resize the heartbeat bar */ + /** + * Resize the heartbeat bar + * @returns {void} + */ resize() { if (this.$refs.wrap) { this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)); @@ -251,8 +252,8 @@ export default { /** * Get the title of the beat. * Used as the hover tooltip on the heartbeat bar. - * @param {Object} beat Beat to get title from - * @returns {string} + * @param {object} beat Beat to get title from + * @returns {string} Beat title */ getBeatTitle(beat) { return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); diff --git a/src/components/HiddenInput.vue b/src/components/HiddenInput.vue index fb86a398c..85ff88372 100644 --- a/src/components/HiddenInput.vue +++ b/src/components/HiddenInput.vue @@ -74,11 +74,17 @@ export default { }, methods: { - /** Show users input in plain text */ + /** + * Show users input in plain text + * @returns {void} + */ showInput() { this.visibility = "text"; }, - /** Censor users input */ + /** + * Censor users input + * @returns {void} + */ hideInput() { this.visibility = "password"; }, diff --git a/src/components/Login.vue b/src/components/Login.vue index ec1040184..2661ab098 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -64,7 +64,10 @@ export default { }, methods: { - /** Submit the user details and attempt to log in */ + /** + * Submit the user details and attempt to log in + * @returns {void} + */ submit() { this.processing = true; diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue index fd6fd1a4b..d66bbe6c1 100644 --- a/src/components/MonitorList.vue +++ b/src/components/MonitorList.vue @@ -103,6 +103,7 @@ export default { * Improve the sticky appearance of the list by increasing its * height as user scrolls down. * Not used on mobile. + * @returns {object} Style for monitor list */ boxStyle() { if (window.innerWidth > 550) { @@ -119,8 +120,7 @@ export default { /** * Returns a sorted list of monitors based on the applied filters and search text. - * - * @return {Array} The sorted list of monitors. + * @returns {Array} The sorted list of monitors. */ sortedMonitorList() { let result = Object.values(this.$root.monitorList); @@ -221,8 +221,7 @@ export default { /** * Determines if any filters are active. - * - * @return {boolean} True if any filter is active, false otherwise. + * @returns {boolean} True if any filter is active, false otherwise. */ filtersActive() { return this.filterState.status != null || this.filterState.active != null || this.filterState.tags != null || this.searchText !== ""; @@ -267,7 +266,10 @@ export default { window.removeEventListener("scroll", this.onScroll); }, methods: { - /** Handle user scroll */ + /** + * Handle user scroll + * @returns {void} + */ onScroll() { if (window.top.scrollY <= 133) { this.windowTop = window.top.scrollY; @@ -283,13 +285,17 @@ export default { monitorURL(id) { return getMonitorRelativeURL(id); }, - /** Clear the search bar */ + /** + * Clear the search bar + * @returns {void} + */ clearSearchText() { this.searchText = ""; }, /** * Update the MonitorList Filter * @param {object} newFilter Object with new filter + * @returns {void} */ updateFilter(newFilter) { this.filterState = newFilter; @@ -297,6 +303,7 @@ export default { /** * Deselect a monitor * @param {number} id ID of monitor + * @returns {void} */ deselect(id) { delete this.selectedMonitors[id]; @@ -304,6 +311,7 @@ export default { /** * Select a monitor * @param {number} id ID of monitor + * @returns {void} */ select(id) { this.selectedMonitors[id] = true; @@ -311,21 +319,30 @@ export default { /** * Determine if monitor is selected * @param {number} id ID of monitor - * @returns {bool} + * @returns {bool} Is the monitor selected? */ isSelected(id) { return id in this.selectedMonitors; }, - /** Disable select mode and reset selection */ + /** + * Disable select mode and reset selection + * @returns {void} + */ cancelSelectMode() { this.selectMode = false; this.selectedMonitors = {}; }, - /** Show dialog to confirm pause */ + /** + * Show dialog to confirm pause + * @returns {void} + */ pauseDialog() { this.$refs.confirmPause.show(); }, - /** Pause each selected monitor */ + /** + * Pause each selected monitor + * @returns {void} + */ pauseSelected() { Object.keys(this.selectedMonitors) .filter(id => this.$root.monitorList[id].active) @@ -333,7 +350,10 @@ export default { this.cancelSelectMode(); }, - /** Resume each selected monitor */ + /** + * Resume each selected monitor + * @returns {void} + */ resumeSelected() { Object.keys(this.selectedMonitors) .filter(id => !this.$root.monitorList[id].active) diff --git a/src/components/MonitorListItem.vue b/src/components/MonitorListItem.vue index 4013b1e38..5b8449686 100644 --- a/src/components/MonitorListItem.vue +++ b/src/components/MonitorListItem.vue @@ -189,7 +189,9 @@ export default { }, methods: { /** - * Changes the collapsed value of the current monitor and saves it to local storage + * Changes the collapsed value of the current monitor and saves + * it to local storage + * @returns {void} */ changeCollapsed() { this.isCollapsed = !this.isCollapsed; @@ -214,6 +216,7 @@ export default { }, /** * Toggle selection of monitor + * @returns {void} */ toggleSelection() { if (this.isSelected(this.monitor.id)) { diff --git a/src/components/MonitorSettingDialog.vue b/src/components/MonitorSettingDialog.vue index d8e9da1a7..e6b2cd1ef 100644 --- a/src/components/MonitorSettingDialog.vue +++ b/src/components/MonitorSettingDialog.vue @@ -67,8 +67,9 @@ export default { methods: { /** * Setting monitor - * @param {Object} group Data of monitor - * @param {Object} monitor Data of monitor + * @param {object} group Data of monitor + * @param {object} monitor Data of monitor + * @returns {void} */ show(group, monitor) { this.monitor = { @@ -86,6 +87,7 @@ export default { * Toggle the value of sendUrl * @param {number} groupIndex Index of group monitor is member of * @param {number} index Index of monitor within group + * @returns {void} */ toggleLink(groupIndex, index) { this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl = !this.$root.publicGroupList[groupIndex].monitorList[index].sendUrl; @@ -95,10 +97,10 @@ export default { * Should a link to the monitor be shown? * Attempts to guess if a link should be shown based upon if * sendUrl is set and if the URL is default or not. - * @param {Object} monitor Monitor to check - * @param {boolean} [ignoreSendUrl=false] Should the presence of the sendUrl + * @param {object} monitor Monitor to check + * @param {boolean} ignoreSendUrl Should the presence of the sendUrl * property be ignored. This will only work in edit mode. - * @returns {boolean} + * @returns {boolean} Should the link be shown? */ showLink(monitor, ignoreSendUrl = false) { // We must check if there are any elements in monitorList to diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index f7fce0d31..170a1d4cf 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -218,7 +218,10 @@ export default { }, methods: { - /** Show dialog to confirm deletion */ + /** + * Show dialog to confirm deletion + * @returns {void} + */ deleteConfirm() { this.modal.hide(); this.$refs.confirmDelete.show(); @@ -227,6 +230,7 @@ export default { /** * Show settings for specified notification * @param {number} notificationID ID of notification to show + * @returns {void} */ show(notificationID) { if (notificationID) { @@ -250,7 +254,10 @@ export default { this.modal.show(); }, - /** Submit the form to the server */ + /** + * Submit the form to the server + * @returns {void} + */ submit() { this.processing = true; this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => { @@ -269,7 +276,10 @@ export default { }); }, - /** Test the notification endpoint */ + /** + * Test the notification endpoint + * @returns {void} + */ test() { this.processing = true; this.$root.getSocket().emit("testNotification", this.notification, (res) => { @@ -278,7 +288,10 @@ export default { }); }, - /** Delete the notification endpoint */ + /** + * Delete the notification endpoint + * @returns {void} + */ deleteNotification() { this.processing = true; this.$root.getSocket().emit("deleteNotification", this.id, (res) => { @@ -293,7 +306,8 @@ export default { /** * Get a unique default name for the notification * @param {keyof NotificationFormList} notificationKey - * @return {string} + * Notification to retrieve + * @returns {string} Default name */ getUniqueDefaultName(notificationKey) { diff --git a/src/components/ProxyDialog.vue b/src/components/ProxyDialog.vue index 74e5878b3..fc92359b9 100644 --- a/src/components/ProxyDialog.vue +++ b/src/components/ProxyDialog.vue @@ -131,7 +131,10 @@ export default { }, methods: { - /** Show dialog to confirm deletion */ + /** + * Show dialog to confirm deletion + * @returns {void} + */ deleteConfirm() { this.modal.hide(); this.$refs.confirmDelete.show(); @@ -140,6 +143,7 @@ export default { /** * Show settings for specified proxy * @param {number} proxyID ID of proxy to show + * @returns {void} */ show(proxyID) { if (proxyID) { @@ -169,7 +173,10 @@ export default { this.modal.show(); }, - /** Submit form data for saving */ + /** + * Submit form data for saving + * @returns {void} + */ submit() { this.processing = true; this.$root.getSocket().emit("addProxy", this.proxy, this.id, (res) => { @@ -187,7 +194,10 @@ export default { }); }, - /** Delete this proxy */ + /** + * Delete this proxy + * @returns {void} + */ deleteProxy() { this.processing = true; this.$root.getSocket().emit("deleteProxy", this.id, (res) => { diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index ba2230f0e..ce0a645b9 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -131,6 +131,7 @@ export default { /** * Remove the specified group * @param {number} index Index of group to remove + * @returns {void} */ removeGroup(index) { this.$root.publicGroupList.splice(index, 1); @@ -141,6 +142,7 @@ export default { * @param {number} groupIndex Index of group to remove monitor * from * @param {number} index Index of monitor to remove + * @returns {void} */ removeMonitor(groupIndex, index) { this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1); @@ -150,10 +152,10 @@ export default { * Should a link to the monitor be shown? * Attempts to guess if a link should be shown based upon if * sendUrl is set and if the URL is default or not. - * @param {Object} monitor Monitor to check - * @param {boolean} [ignoreSendUrl=false] Should the presence of the sendUrl + * @param {object} monitor Monitor to check + * @param {boolean} ignoreSendUrl Should the presence of the sendUrl * property be ignored. This will only work in edit mode. - * @returns {boolean} + * @returns {boolean} Should the link be shown */ showLink(monitor, ignoreSendUrl = false) { // We must check if there are any elements in monitorList to @@ -166,8 +168,8 @@ export default { /** * Returns formatted certificate expiry or Bad cert message - * @param {Object} monitor Monitor to show expiry for - * @returns {string} + * @param {object} monitor Monitor to show expiry for + * @returns {string} Certificate expiry message */ formattedCertExpiryMessage(monitor) { if (monitor?.element?.validCert && monitor?.element?.certExpiryDaysRemaining) { @@ -180,9 +182,9 @@ export default { }, /** - * Returns certificate expiry based on days remaining - * @param {Object} monitor Monitor to show expiry for - * @returns {string} + * Returns certificate expiry color based on days remaining + * @param {object} monitor Monitor to show expiry for + * @returns {string} Color for certificate expiry */ certExpiryColor(monitor) { if (monitor?.element?.validCert && monitor.element.certExpiryDaysRemaining > 7) { diff --git a/src/components/Tag.vue b/src/components/Tag.vue index 956eea3e7..6c2ff8c9b 100644 --- a/src/components/Tag.vue +++ b/src/components/Tag.vue @@ -19,12 +19,13 @@ + + diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 606d96578..f9c2be27f 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -452,6 +452,7 @@ export default { /** * If the monitor is added to public list, which will not be in this list. + * @returns {object[]} List of monitors */ sortedMonitorList() { let result = []; @@ -596,7 +597,8 @@ export default { /** * If connected to the socket and logged in, request private data of this statusPage - * @param connected + * @param {boolean} loggedIn Is the client logged in? + * @returns {void} */ "$root.loggedIn"(loggedIn) { if (loggedIn) { @@ -619,6 +621,8 @@ export default { /** * Selected a monitor and add to the list. + * @param {object} monitor Monitor to add + * @returns {void} */ selectedMonitor(monitor) { if (monitor) { @@ -723,7 +727,7 @@ export default { /** * Get status page data * It should be preloaded in window.preloadData - * @returns {Promise} + * @returns {Promise} Status page data */ getData: function () { if (window.preloadData) { @@ -738,13 +742,16 @@ export default { /** * Provide syntax highlighting for CSS * @param {string} code Text to highlight - * @returns {string} + * @returns {string} Highlighted HTML */ highlighter(code) { return highlight(code, languages.css); }, - /** Update the heartbeat list and update favicon if neccessary */ + /** + * Update the heartbeat list and update favicon if necessary + * @returns {void} + */ updateHeartbeatList() { // If editMode, it will use the data from websocket. if (! this.editMode) { @@ -792,7 +799,10 @@ export default { }, 1000); }, - /** Enable editing mode */ + /** + * Enable editing mode + * @returns {void} + */ edit() { if (this.hasToken) { this.$root.initSocketIO(true); @@ -804,7 +814,10 @@ export default { } }, - /** Save the status page */ + /** + * Save the status page + * @returns {void} + */ save() { let startTime = new Date(); this.config.slug = this.config.slug.trim().toLowerCase(); @@ -832,12 +845,18 @@ export default { }); }, - /** Show dialog confirming deletion */ + /** + * Show dialog confirming deletion + * @returns {void} + */ deleteDialog() { this.$refs.confirmDelete.show(); }, - /** Request deletion of this status page */ + /** + * Request deletion of this status page + * @returns {void} + */ deleteStatusPage() { this.$root.getSocket().emit("deleteStatusPage", this.slug, (res) => { if (res.ok) { @@ -850,15 +869,18 @@ export default { }, /** - * Returns label for a specifed monitor - * @param {Object} monitor Object representing monitor - * @returns {string} + * Returns label for a specified monitor + * @param {object} monitor Object representing monitor + * @returns {string} Monitor label */ monitorSelectorLabel(monitor) { return `${monitor.name}`; }, - /** Add a group to the status page */ + /** + * Add a group to the status page + * @returns {void} + */ addGroup() { let groupName = this.$t("Untitled Group"); @@ -872,12 +894,18 @@ export default { }); }, - /** Add a domain to the status page */ + /** + * Add a domain to the status page + * @returns {void} + */ addDomainField() { this.config.domainNameList.push(""); }, - /** Discard changes to status page */ + /** + * Discard changes to status page + * @returns {void} + */ discard() { location.href = "/status/" + this.slug; }, @@ -885,19 +913,26 @@ export default { /** * Set URL of new image after successful crop operation * @param {string} imgDataUrl URL of image in data:// format + * @returns {void} */ cropSuccess(imgDataUrl) { this.imgDataUrl = imgDataUrl; }, - /** Show image crop dialog if in edit mode */ + /** + * Show image crop dialog if in edit mode + * @returns {void} + */ showImageCropUploadMethod() { if (this.editMode) { this.showImageCropUpload = true; } }, - /** Create an incident for this status page */ + /** + * Create an incident for this status page + * @returns {void} + */ createIncident() { this.enableEditIncidentMode = true; @@ -912,7 +947,10 @@ export default { }; }, - /** Post the incident to the status page */ + /** + * Post the incident to the status page + * @returns {void} + */ postIncident() { if (this.incident.title === "" || this.incident.content === "") { toast.error(this.$t("Please input title and content")); @@ -932,13 +970,19 @@ export default { }, - /** Click Edit Button */ + /** + * Click Edit Button + * @returns {void} + */ editIncident() { this.enableEditIncidentMode = true; this.previousIncident = Object.assign({}, this.incident); }, - /** Cancel creation or editing of incident */ + /** + * Cancel creation or editing of incident + * @returns {void} + */ cancelIncident() { this.enableEditIncidentMode = false; @@ -948,7 +992,10 @@ export default { } }, - /** Unpin the incident */ + /** + * Unpin the incident + * @returns {void} + */ unpinIncident() { this.$root.getSocket().emit("unpinIncident", this.slug, () => { this.incident = null; @@ -957,7 +1004,8 @@ export default { /** * Get the relative time difference of a date from now - * @returns {string} + * @param {any} date Date to get time difference + * @returns {string} Time difference */ dateFromNow(date) { return dayjs.utc(date).fromNow(); @@ -966,6 +1014,7 @@ export default { /** * Remove a domain from the status page * @param {number} index Index of domain to remove + * @returns {void} */ removeDomain(index) { this.config.domainNameList.splice(index, 1); @@ -973,7 +1022,7 @@ export default { /** * Generate sanitized HTML from maintenance description - * @param {string} description + * @param {string} description Text to sanitize * @returns {string} Sanitized HTML */ maintenanceHTML(description) { diff --git a/src/router.js b/src/router.js index a8644805e..e77a1137d 100644 --- a/src/router.js +++ b/src/router.js @@ -19,6 +19,7 @@ import DockerHosts from "./components/settings/Docker.vue"; import MaintenanceDetails from "./pages/MaintenanceDetails.vue"; import ManageMaintenance from "./pages/ManageMaintenance.vue"; import APIKeys from "./components/settings/APIKeys.vue"; +import SetupDatabase from "./pages/SetupDatabase.vue"; // Settings - Sub Pages import Appearance from "./components/settings/Appearance.vue"; @@ -167,6 +168,10 @@ const routes = [ path: "/setup", component: Setup, }, + { + path: "/setup-database", + component: SetupDatabase, + }, { path: "/status-page", component: StatusPage, diff --git a/src/util-frontend.js b/src/util-frontend.js index 4b85fa346..7ebd918f2 100644 --- a/src/util-frontend.js +++ b/src/util-frontend.js @@ -4,6 +4,7 @@ import { localeDirection, currentLocale } from "./i18n"; /** * Returns the offset from UTC in hours for the current locale. + * @param {string} timeZone Timezone to get offset for * @returns {number} The offset from UTC in hours. * * Generated by Trelent @@ -20,12 +21,11 @@ function getTimezoneOffset(timeZone) { } /** -* Returns a list of timezones sorted by their offset from UTC. -* @param {Object[]} timezones An array of timezone objects. -* @returns {Object[]} A list of the given timezones sorted by their offset from UTC. -* -* Generated by Trelent -*/ + * Returns a list of timezones sorted by their offset from UTC. + * @returns {object[]} A list of the given timezones sorted by their offset from UTC. + * + * Generated by Trelent + */ export function timezoneList() { let result = []; @@ -58,7 +58,10 @@ export function timezoneList() { return result; } -/** Set the locale of the HTML page */ +/** + * Set the locale of the HTML page + * @returns {void} + */ export function setPageLocale() { const html = document.documentElement; html.setAttribute("lang", currentLocale() ); @@ -68,7 +71,7 @@ export function setPageLocale() { /** * Get the base URL * Mainly used for dev, because the backend and the frontend are in different ports. - * @returns {string} + * @returns {string} Base URL */ export function getResBaseURL() { const env = process.env.NODE_ENV; @@ -81,6 +84,10 @@ export function getResBaseURL() { } } +/** + * Are we currently running in a dev container? + * @returns {boolean} Running in dev container? + */ export function isDevContainer() { // eslint-disable-next-line no-undef return (typeof DEVCONTAINER === "string" && DEVCONTAINER === "1"); @@ -88,6 +95,7 @@ export function isDevContainer() { /** * Supports GitHub Codespaces only currently + * @returns {string} Dev container server hostname */ export function getDevContainerServerHostname() { if (!isDevContainer()) { @@ -99,9 +107,10 @@ export function getDevContainerServerHostname() { } /** - * - * @param {} mqtt wheather or not the regex should take into account the fact that it is an mqtt uri - * @returns RegExp The requested regex + * Regex pattern fr identifying hostnames and IP addresses + * @param {boolean} mqtt whether or not the regex should take into + * account the fact that it is an mqtt uri + * @returns {RegExp} The requested regex */ export function hostNameRegexPattern(mqtt = false) { // mqtt, mqtts, ws and wss schemes accepted by mqtt.js (https://github.com/mqttjs/MQTT.js/#connect) @@ -117,7 +126,8 @@ export function hostNameRegexPattern(mqtt = false) { /** * Get the tag color options * Shared between components - * @returns {Object[]} + * @param {any} self Component + * @returns {object[]} Colour options */ export function colorOptions(self) { return [ diff --git a/test/backend-test-entry.js b/test/backend-test-entry.js new file mode 100644 index 000000000..7cc8d734f --- /dev/null +++ b/test/backend-test-entry.js @@ -0,0 +1,20 @@ +// Check Node.js version +const semver = require("semver"); +const childProcess = require("child_process"); + +const nodeVersion = process.versions.node; +console.log("Node.js version: " + nodeVersion); + + + +// Node.js version >= 18 +if (semver.satisfies(nodeVersion, ">= 18")) { + console.log("Use the native test runner: `node --test`"); + childProcess.execSync("npm run test-backend:18", { stdio: "inherit" }); +} else { + // 14 - 16 here + console.log("Use `test` package: `node--test`") + childProcess.execSync("npm run test-backend:14", { stdio: "inherit" }); +} + + diff --git a/test/backend-test/test-uptime-calculator.js b/test/backend-test/test-uptime-calculator.js new file mode 100644 index 000000000..b56ac946d --- /dev/null +++ b/test/backend-test/test-uptime-calculator.js @@ -0,0 +1,423 @@ +const semver = require("semver"); +let test; +const nodeVersion = process.versions.node; +// Node.js version >= 18 +if (semver.satisfies(nodeVersion, ">= 18")) { + test = require("node:test"); +} else { + test = require("test"); +} + +const assert = require("node:assert"); +const { UptimeCalculator } = require("../../server/uptime-calculator"); +const dayjs = require("dayjs"); +const { UP, DOWN, PENDING, MAINTENANCE } = require("../../src/util"); +dayjs.extend(require("dayjs/plugin/utc")); +dayjs.extend(require("../../server/modules/dayjs/plugin/timezone")); +dayjs.extend(require("dayjs/plugin/customParseFormat")); + +test("Test Uptime Calculator - custom date", async (t) => { + let c1 = new UptimeCalculator(); + + // Test custom date + UptimeCalculator.currentDate = dayjs.utc("2021-01-01T00:00:00.000Z"); + assert.strictEqual(c1.getCurrentDate().unix(), dayjs.utc("2021-01-01T00:00:00.000Z").unix()); +}); + +test("Test update - UP", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + let c2 = new UptimeCalculator(); + let date = await c2.update(UP); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:46:59").unix()); +}); + +test("Test update - MAINTENANCE", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(MAINTENANCE); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); +}); + +test("Test update - DOWN", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(DOWN); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); +}); + +test("Test update - PENDING", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(PENDING); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); +}); + +test("Test flatStatus", async (t) => { + let c2 = new UptimeCalculator(); + assert.strictEqual(c2.flatStatus(UP), UP); + //assert.strictEqual(c2.flatStatus(MAINTENANCE), UP); + assert.strictEqual(c2.flatStatus(DOWN), DOWN); + assert.strictEqual(c2.flatStatus(PENDING), DOWN); +}); + +test("Test getMinutelyKey", async (t) => { + let c2 = new UptimeCalculator(); + let divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:00")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + + // Edge case 1 + c2 = new UptimeCalculator(); + divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:01")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + + // Edge case 2 + c2 = new UptimeCalculator(); + divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:59")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); +}); + +test("Test getDailyKey", async (t) => { + let c2 = new UptimeCalculator(); + let dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 20:46:00").unix()); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:45:30").unix()); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + // Edge case 1 + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:59:59").unix()); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + // Edge case 2 + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 00:00:00").unix()); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); +}); + +test("Test lastDailyUptimeData", async (t) => { + let c2 = new UptimeCalculator(); + await c2.update(UP); + assert.strictEqual(c2.lastDailyUptimeData.up, 1); +}); + +test("Test get24Hour Uptime and Avg Ping", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // No data + let c2 = new UptimeCalculator(); + let data = c2.get24Hour(); + assert.strictEqual(data.uptime, 0); + assert.strictEqual(data.avgPing, null); + + // 1 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 100); + let uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 100); + + // 2 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 100); + await c2.update(UP, 200); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 150); + + // 3 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 0); + await c2.update(UP, 100); + await c2.update(UP, 400); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 166.66666666666666); + + // 1 MAINTENANCE + c2 = new UptimeCalculator(); + await c2.update(MAINTENANCE); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 1 PENDING + c2 = new UptimeCalculator(); + await c2.update(PENDING); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 2 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 1 DOWN, 1 UP + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(UP, 0.5); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.5); + assert.strictEqual(c2.get24Hour().avgPing, 0.5); + + // 1 UP, 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(UP, 123); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.5); + assert.strictEqual(c2.get24Hour().avgPing, 123); + + // Add 24 hours + c2 = new UptimeCalculator(); + await c2.update(UP, 0); + await c2.update(UP, 0); + await c2.update(UP, 0); + await c2.update(UP, 1); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); + + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); + + // After 24 hours, even if there is no data, the uptime should be still 80% + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); + + // Add more 24 hours (48 hours) + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); + + // After 48 hours, even if there is no data, the uptime should be still 80% + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); +}); + +test("Test get7DayUptime", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // No data + let c2 = new UptimeCalculator(); + let uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); + + // 2 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); + + // 3 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); + + // 1 MAINTENANCE + c2 = new UptimeCalculator(); + await c2.update(MAINTENANCE); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 PENDING + c2 = new UptimeCalculator(); + await c2.update(PENDING); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 2 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 DOWN, 1 UP + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.5); + + // 1 UP, 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.5); + + // Add 7 days + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.8); + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(7, "day"); + + // After 7 days, even if there is no data, the uptime should be still 80% + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.8); + +}); + +test("Test get30DayUptime (1 check per day)", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + let c2 = new UptimeCalculator(); + let uptime = c2.get30Day().uptime; + assert.strictEqual(uptime, 0); + + let up = 0; + let down = 0; + let flip = true; + for (let i = 0; i < 30; i++) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); + + if (flip) { + await c2.update(UP); + up++; + } else { + await c2.update(DOWN); + down++; + } + + uptime = c2.get30Day().uptime; + assert.strictEqual(uptime, up / (up + down)); + + flip = !flip; + } + + // Last 7 days + // Down, Up, Down, Up, Down, Up, Down + // So 3 UP + assert.strictEqual(c2.get7Day().uptime, 3 / 7); +}); + +test("Test get1YearUptime (1 check per day)", async (t) => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + let c2 = new UptimeCalculator(); + let uptime = c2.get1Year().uptime; + assert.strictEqual(uptime, 0); + + let flip = true; + for (let i = 0; i < 365; i++) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); + + if (flip) { + await c2.update(UP); + } else { + await c2.update(DOWN); + } + + uptime = c2.get30Day().time; + flip = !flip; + } + + assert.strictEqual(c2.get1Year().uptime, 183 / 365); + assert.strictEqual(c2.get30Day().uptime, 15 / 30); + assert.strictEqual(c2.get7Day().uptime, 4 / 7); +}); + +/** + * Code from here: https://stackoverflow.com/a/64550489/1097815 + */ +function memoryUsage() { + const formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`; + const memoryData = process.memoryUsage(); + + const memoryUsage = { + rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`, + heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`, + heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`, + external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`, + }; + return memoryUsage; +} + +test("Worst case", async (t) => { + console.log("Memory usage before preparation", memoryUsage()); + + let c = new UptimeCalculator(); + let up = 0; + let down = 0; + let interval = 20; + + await t.test("Prepare data", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // Since 2023-08-12 will be out of 365 range, it starts from 2023-08-13 actually + let actualStartDate = dayjs.utc("2023-08-13 00:00:00").unix(); + + // Simulate 1s interval for a year + for (let i = 0; i < 365 * 24 * 60 * 60; i += interval) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(interval, "second"); + + //Randomly UP, DOWN, MAINTENANCE, PENDING + let rand = Math.random(); + if (rand < 0.25) { + c.update(UP); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + up++; + } + } else if (rand < 0.5) { + c.update(DOWN); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + down++; + } + } else if (rand < 0.75) { + c.update(MAINTENANCE); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + //up++; + } + } else { + c.update(PENDING); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + down++; + } + } + } + console.log("Final Date: ", UptimeCalculator.currentDate.format("YYYY-MM-DD HH:mm:ss")); + console.log("Memory usage before preparation", memoryUsage()); + + assert.strictEqual(c.minutelyUptimeDataList.length(), 1440); + assert.strictEqual(c.dailyUptimeDataList.length(), 365); + }); + + await t.test("get1YearUptime()", async () => { + assert.strictEqual(c.get1Year().uptime, up / (up + down)); + }); + +}); diff --git a/test/backend.spec.js b/test/backend.spec.js index bc5ee810c..2035c6840 100644 --- a/test/backend.spec.js +++ b/test/backend.spec.js @@ -235,13 +235,13 @@ describe("The function filterAndJoin", () => { describe("Test uptimeKumaServer.getClientIP()", () => { it("should able to get a correct client IP", async () => { - Database.init({ + Database.initDataDir({ "data-dir": "./data/test" }); - if (! fs.existsSync(Database.path)) { + if (! fs.existsSync(Database.sqlitePath)) { log.info("server", "Copying Database"); - fs.copyFileSync(Database.templatePath, Database.path); + fs.copyFileSync(Database.templatePath, Database.sqlitePath); } await Database.connect(true);