From 022a94353ceb2513ffb2a6f0033ba5e5c73f8dfe Mon Sep 17 00:00:00 2001 From: Alex Layton Date: Fri, 20 Sep 2024 17:29:46 -0400 Subject: [PATCH] cleanup metrics --- .eslintrc.yaml | 10 ++++- README.md | 2 +- package.json | 8 ++-- src/Job.ts | 7 +++ src/Queue.ts | 23 +++++----- src/Runner.ts | 49 +++++++++++++-------- src/Service.ts | 104 +++++++++++++------------------------------- test/all.test.ts | 8 ++-- test/report.test.ts | 10 ++--- test/utils.ts | 11 ++--- tsconfig.json | 5 +-- yarn.lock | 85 ++++++++++++++++++------------------ 12 files changed, 157 insertions(+), 165 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index c351fe2..404bd3d 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -37,7 +37,7 @@ parser: '@typescript-eslint/parser' parserOptions: ecmaVersion: 2020 - project: './**/tsconfig.*' + project: ./**/tsconfig.json overrides: - files: '*.{c,m,}ts' @@ -48,7 +48,7 @@ overrides: - prettier parserOptions: ecmaVersion: 2020 - project: './**/tsconfig.*' + project: ./**/tsconfig.json rules: '@typescript-eslint/naming-convention': [ @@ -78,6 +78,12 @@ overrides: leadingUnderscore: require, }, + { + selector: memberLike, + modifiers: [static, readonly], + format: [PascalCase], + }, + { selector: typeLike, format: [PascalCase] }, { selector: parameter, modifiers: [destructured], format: null }, diff --git a/README.md b/README.md index 633adaf..e87784f 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ service.on( }); return { coolThing: r.data.thing }; - } + }, ); service.start().catch((e: unknown) => { diff --git a/package.json b/package.json index d6fe901..967a590 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "serialize-error": "^11.0.3", "tiny-json-http": "^7.5.1", "tslib": "^2.6.2", - "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz" + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" }, "peerDependencies": { "@oada/client": "^4.5.5" @@ -96,6 +96,7 @@ "@commitlint/config-conventional": "^18.6.0", "@oada/client": "^5.0.0", "@tsconfig/node16": "^16.1.1", + "@tsconfig/node20": "^20.1.4", "@types/chai": "^4.3.11", "@types/clone-deep": "^4.0.4", "@types/convict": "^6.1.6", @@ -146,6 +147,7 @@ "node": "20.11.0" }, "resolutions": { - "xksuid": "https://github.com/g12i/xksuid.git#fix-crypto-polyfill" + "xksuid": "https://github.com/g12i/xksuid.git#fix-crypto-polyfill", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" } -} \ No newline at end of file +} diff --git a/src/Job.ts b/src/Job.ts index 5c2906a..861e5a4 100644 --- a/src/Job.ts +++ b/src/Job.ts @@ -42,6 +42,13 @@ export interface JobUpdate { * Holds job data */ export class Job { + static readonly Statuses = [ + 'queued', + 'running', + 'success', + 'failure', + ] as const; + /** * Fetch a Job from an OADA resource ID * @param oada Authenticated OADAClient to fetch Job object diff --git a/src/Queue.ts b/src/Queue.ts index 69988b1..1eaa299 100644 --- a/src/Queue.ts +++ b/src/Queue.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import PQueue from 'p-queue'; import moment from 'moment'; import type { Link } from '@oada/types/oada.js'; @@ -24,12 +25,10 @@ import type OADAJobsChange from '@oada/types/oada/service/jobs-change.js'; // TODO: Fix this type and get it back in here // import { assert as assertJobs } from '@oada/types/oada/service/jobs-change.js'; +import { error, info, stripResource, trace } from './utils.js'; import { Job } from './Job.js'; -import PQueue from 'p-queue'; import { Runner } from './Runner.js'; import type { Service } from './Service.js'; - -import { error, info, stripResource, trace } from './utils.js'; import { tree } from './tree.js'; /** @@ -39,6 +38,7 @@ export class Queue { #watchRequestId: string | string[] = ''; readonly #oada: OADAClient; readonly #queue: PQueue; + readonly #service; /** * Creates queue watcher @@ -46,12 +46,13 @@ export class Queue { * @param token? The token for the queue to watch, or undefined if an OADA client was passed */ constructor( - private readonly _service: Service, + _service: Service, private readonly _id: string, ) { // , token?: string) { + this.#service = _service; this.#oada = _service.getClient(); // .clone(token ?? ''); - this.#queue = new PQueue({ concurrency: this._service.concurrency }); + this.#queue = new PQueue({ concurrency: this.#service.concurrency }); /* If (typeof domainOrOada === 'string') { this.oada = service.getClient().clone(token ?? ''); @@ -70,7 +71,7 @@ export class Queue { * Opens the WATCH and begins processing jobs */ public async start(skipQueue = false): Promise { - const root = `/bookmarks/services/${this._service.name}`; + const root = `/bookmarks/services/${this.#service.name}`; const jobspath = `${root}/jobs/pending`; const successpath = `${root}/jobs/success`; const failurepath = `${root}/jobs/failure`; @@ -199,17 +200,19 @@ export class Queue { async #doJobs(jobs: OADAJobs | OADAJobsChange): Promise { // Queue up the Runners in parallel for (const [jobKey, value] of Object.entries(jobs)) { - this._service.metrics[`${this._service.name}_total_queued`].inc(); void this.#queue.add(async () => { const { _id } = value as Link; if (!_id) return; // Fetch the job const { job, isJob } = await Job.fromOada(this.#oada, _id); - const mtype = job.type.replaceAll('-', '_').replaceAll(' ', '_'); - this._service.metrics[`${this._service.name}_queued_${mtype}`].inc(); + this.#service.metrics.inc({ + service: this.#service.name, + type: job.type, + state: 'queued', + }); // Instantiate a runner to manage the job - const runner = new Runner(this._service, jobKey, job, this.#oada); + const runner = new Runner(this.#service, jobKey, job, this.#oada); if (!isJob) { await runner.finish('failure', {}, moment()); diff --git a/src/Runner.ts b/src/Runner.ts index 060db56..beb1079 100644 --- a/src/Runner.ts +++ b/src/Runner.ts @@ -31,6 +31,7 @@ import { tree } from './tree.js'; import { onFinish as slackOnFinish } from './finishReporters/slack/index.js'; export class JobError extends Error { + // eslint-disable-next-line @typescript-eslint/naming-convention 'JobError'?: string; 'constructor'(m: string, t?: string) { super(m); @@ -68,9 +69,11 @@ export class Runner { * appropriate. */ public async run(): Promise { - const mtype = this.#job.type.replaceAll('-', '_').replaceAll(' ', '_'); - this.#service.metrics[`${this.#service.name}_running_${mtype}`].inc(); - this.#service.metrics[`${this.#service.name}_total_running`].inc(); + this.#service.metrics.inc({ + service: this.#service.name, + type: this.#job.type, + state: 'running', + }); // A quick check to ensure job isn't already completed if (this.#job.status === 'success' || this.#job.status === 'failure') { @@ -125,7 +128,7 @@ export class Runner { trace(`[job ${this.#job.oadaId}] Error: %O`, error_); await (error_ instanceof TimeoutError - ? this.finish('failure', error_, moment(), 'timeout') + ? this.finish('failure', error_ as unknown as Json, moment(), 'timeout') : this.finish('failure', error_, moment(), error_.JobError)); } } @@ -180,7 +183,8 @@ export class Runner { } trace( - `[job ${this.#job.oadaId} ]: putting to job resource the final {status,result} = ${data}`, + { job: this.#job.oadaId, ...data }, + 'Putting to job resource the final status/result', ); await this.#oada.put({ path: `/${this.#job.oadaId}`, @@ -195,12 +199,12 @@ export class Runner { // Link into success/failure event log const date = moment(time).format('YYYY-MM-DD'); - let finalpath = `/bookmarks/services/${this.#service.name}/jobs/${status}/day-index/${date}`; + const finalpath = `/bookmarks/services/${this.#service.name}/jobs/${status}/day-index/${date}`; info( `[job ${this.#job.oadaId} ]: linking job to final resting place at ${finalpath}`, ); await this.#oada.put({ - path: finalpath!, + path: finalpath, data: { [this.#jobKey]: { _id: this.#job.oadaId, @@ -212,12 +216,12 @@ export class Runner { // If there is a failType, also link to the typed failure log if (status === 'failure' && failType) { - let typedFailPath = `/bookmarks/services/${this.#service.name}/jobs/typed-${status}/${failType}/day-index/${date}`; + const typedFailPath = `/bookmarks/services/${this.#service.name}/jobs/typed-${status}/${failType}/day-index/${date}`; info( `[job ${this.#job.oadaId} ]: linking job to final resting place at ${typedFailPath}`, ); await this.#oada.put({ - path: typedFailPath!, + path: typedFailPath, data: { [this.#jobKey]: { _id: this.#job.oadaId, @@ -238,15 +242,26 @@ export class Runner { `[job ${this.#job.oadaId} ]: marking job as ${status}`, failType ?? 'unknown', ); - const mtype = this.#job.type.replaceAll('-', '_').replaceAll(' ', '_'); - this.#service.metrics[`${this.#service.name}_total_queued`].dec(); - this.#service.metrics[`${this.#service.name}_queued_${mtype}`].dec(); - this.#service.metrics[`${this.#service.name}_total_running`].dec(); - this.#service.metrics[`${this.#service.name}_running_${mtype}`].dec(); + // Decrement queued job count? + this.#service.metrics.dec({ + service: this.#service.name, + type: this.#job.type, + state: 'queued', + }); + // Decrement running job count + this.#service.metrics.dec({ + service: this.#service.name, + type: this.#job.type, + state: 'running', + }); - this.#service.metrics[`${this.#service.name}_total_${status}`].inc(); - this.#service.metrics[`${this.#service.name}_${status}_${mtype}`].inc(); + // Increment successes or failures based on status + this.#service.metrics.inc({ + service: this.#service.name, + type: this.#job.type, + state: status, + }); // Notify the status reporter if there is one try { @@ -275,7 +290,7 @@ export class Runner { await slackOnFinish({ config: r, service: this.#service, - finalpath: finalpath!, + finalpath, job: finaljob, jobId: this.#job.oadaId, status, diff --git a/src/Service.ts b/src/Service.ts index 7e87828..71ae0f8 100644 --- a/src/Service.ts +++ b/src/Service.ts @@ -16,13 +16,13 @@ */ import type { Config } from '@oada/client'; +import { Gauge } from '@oada/lib-prom'; import { OADAClient } from '@oada/client'; import { assert as assertQueue } from '@oada/types/oada/service/queue.js'; -import { Gauge } from '@oada/lib-prom'; import { Report, type ReportConstructor } from './Report.js'; import { debug, error, warn } from './utils.js'; -import type { Job } from './Job.js'; +import { type Job } from './Job.js'; import type { Json } from './index.js'; import type { Logger } from './Logger.js'; import { Queue } from './Queue.js'; @@ -64,10 +64,6 @@ export interface ConstructorArguments { opts?: ServiceOptions; } -export interface ServiceMetrics { - [key: string]: any; -} - export const defaultServiceQueueName = 'default-service-queue'; /** @@ -86,7 +82,7 @@ export class Service { public domain: string; public token: string; public opts: ServiceOptions | undefined; - public metrics: ServiceMetrics; + public metrics; readonly #oada: OADAClient; // Readonly #clients = new Map(); @@ -128,8 +124,13 @@ export class Service { this.domain = this.#oada.getDomain(); this.token = this.#oada.getToken()[0]!; this.concurrency = object.concurrency ?? this.#oada.getConcurrency(); - this.metrics = {}; - this.#ensureMetrics(); + + // TODO: Get total pending jobs in collect callback? + this.metrics = new Gauge({ + name: 'oada_jobs_total', + help: 'Number of OADA jobs', + labelNames: ['service', 'type', 'state'] as const, + }); if (object.opts) { this.opts = object.opts; @@ -193,7 +194,6 @@ export class Service { * @param worker Worker function */ public on(type: string, timeout: number, work: WorkerFunction): void { - this.#ensureTypedMetrics(type); this.#workers.set(type, { work, timeout }); } @@ -271,84 +271,42 @@ export class Service { } } - /** - * Create the metrics - */ - async #ensureTypedMetrics(type: string): Promise { - const statuses = ['queued', 'running', 'success', 'failure']; - for (const status of statuses) { - let mtype = type.replaceAll('-', '_').replaceAll(' ', '_'); - const name = `${this.name}_${status}_${mtype}`; - if (!this.metrics[name]) { - this.metrics[name] = new Gauge({ - name: name, - help: `Number of ${this.name} jobs of type "${type}" that are of status "${status}"`, - labelNames: ['service', mtype, status], - }); - this.metrics[name].set(0); - } - } - } - /* - async #initTotalMetrics(): Promise { + Async #initTotalMetrics(): Promise { const date = new Date().toISOString().split('T')[0]; for await (const status of ['success', 'failure']) { try { - let { data } = await this.#oada.get({ - path: `/bookmarks/services/${this.name}/jobs/${status}/day-index/${date}` - }) - let keys = Object.keys(data as Record).filter(key => !key.startsWith('_')); - this.metrics[`${this.name}_total_${status}`].set(keys.length); - } catch(err) { - this.metrics[`${this.name}_total_${status}`].set(0); + const { data } = await this.#oada.get({ + path: `/bookmarks/services/${this.name}/jobs/${status}/day-index/${date}`, + }); + const keys = Object.keys(data as Record).filter( + (key) => !key.startsWith('_'), + ); + this.metrics.set(keys.length); + } catch { + this.metrics.set(0); } } } async #initTypedMetrics(type: string): Promise { - let mtype = type.replaceAll('-', '_').replaceAll(' ', '_'); + const mType = type.replaceAll('-', '_').replaceAll(' ', '_'); const date = new Date().toISOString().split('T')[0]; for await (const status of ['success', 'failure']) { try { - this.metrics[`${this.name}_${status}_${mtype}`].set(0); - let { data } = await this.#oada.get({ - path: `/bookmarks/services/${this.name}/jobs/${status}/day-index/${date}` - }) + this.metrics[`${this.name}_${status}_${mType}`].set(0); + const { data } = await this.#oada.get({ + path: `/bookmarks/services/${this.name}/jobs/${status}/day-index/${date}`, + }); for await (const job of Object.keys(data as Record)) { - let { data: j } = await this.#oada.get({ - path: `/bookmarks/services/${this.name}/jobs/${status}/day-index/${date}/${job}` - }) as unknown as { data: { j: string, [k: string]: any } }; - if (j.type === type) this.metrics[`${this.name}_${status}_${mtype}`].inc(); + const { data: index } = (await this.#oada.get({ + path: `/bookmarks/services/${this.name}/jobs/${status}/day-index/${date}/${job}`, + })) as unknown as { data: { j: string; [k: string]: any } }; + if (index.type === type) + this.metrics[`${this.name}_${status}_${mType}`].inc(); } - } catch(err) { - } + } catch {} } } */ - - async #ensureMetrics(): Promise { - /* - this.#oada.ensure({ - path: `/bookmarks/services/${this.name}/metrics`, - data: {}, - }); - const { data: val } = this.#oada.get({ - path: `/bookmarks/services/${this.name}/metrics`, - }) - */ - - const statuses = ['queued', 'running', 'success', 'failure']; - for (const status of statuses) { - const name = `${this.name}_total_${status}`; - if (!this.metrics[name]) { - this.metrics[name] = new Gauge({ - name: name, - help: `Total number of ${this.name} jobs that are of status "${status}"`, - labelNames: ['service', 'total', status], - }); - this.metrics[name].set(0); - } - } - } } diff --git a/test/all.test.ts b/test/all.test.ts index be9d243..9d2b85c 100644 --- a/test/all.test.ts +++ b/test/all.test.ts @@ -66,13 +66,13 @@ test.before(async (t) => { .then((r) => oadaify(r.data as Json)); if (typeof existing === 'object' && existing) { const testservices = Object.keys(existing).filter((servicename) => - /^JOBSTEST/.exec(servicename) + /^JOBSTEST/.exec(servicename), ); await Promise.all( testservices.map(async (servicename) => { trace('Found old test job service: ', servicename, ', deleting it'); await oada.delete({ path: `/bookmarks/services/${servicename}` }); - }) + }), ); } @@ -146,7 +146,7 @@ test('Should move successful job to success queue, have status success, and stor t.is((result as OADAJob)?.status, 'success'); t.deepEqual((result as OADAJob)?.result, { success: true }); // This is what the basic service handler returns } catch (error_: any) { - if (error_.status === 404) return ; // If it's not there, just return false + if (error_.status === 404) return; // If it's not there, just return false throw error_ as Error; // Any other error, throw it back up } }); @@ -227,4 +227,4 @@ test('Testing jobs change', async (t) => { oada: con, }); }); -}); \ No newline at end of file +}); diff --git a/test/report.test.ts b/test/report.test.ts index d9a2458..4a005ff 100644 --- a/test/report.test.ts +++ b/test/report.test.ts @@ -106,7 +106,7 @@ test.before(async () => { }, }, frequency: `${s} ${min} ${hr} * * *`, - email: () => { + email() { const date = moment().format('YYYY-MM-DD'); return { from: 'noreply@trellis.one', @@ -221,7 +221,7 @@ test('Should post a job to abalonemail when it is time to report', async (t) => t.true(keys.length > 0); const { data: email } = (await oada.get({ - path: `${successJobs}/${date}/${keys[keys.length - 1]}`, + path: `${successJobs}/${date}/${keys.at(-1)}`, })) as unknown as { data: EmailJob }; t.is(email.config.from, 'noreply@trellis.one'); t.is(email.config.subject, `Test Email - ${date}`); @@ -257,7 +257,7 @@ test.only('parseAttachments should be able to reconstruct the csv object', async }, }, frequency: `${dt.getSeconds()} ${dt.getMinutes()} ${dt.getHours()} * * ${dt.getDay()}`, - email: () => { + email() { const date = moment().format('YYYY-MM-DD'); return { from: 'noreply@trellis.one', @@ -325,7 +325,7 @@ test.skip('Should not generate an abalonemail job when the report data is empty' }, }, frequency: `${dt.getSeconds()} ${dt.getMinutes()} ${dt.getHours()} * * ${dt.getDay()}`, - email: () => { + email() { const date = moment().format('YYYY-MM-DD'); return { from: 'noreply@trellis.one', @@ -383,7 +383,7 @@ test.skip('Should generate an abalonemail job when the report data is empty and }, }, frequency: `${dt.getSeconds()} ${dt.getMinutes()} ${dt.getHours()} * * ${dt.getDay()}`, - email: () => { + email() { const date = moment().format('YYYY-MM-DD'); return { from: 'noreply@trellis.one', diff --git a/test/utils.ts b/test/utils.ts index def5c1c..8dd1448 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -21,7 +21,7 @@ import { tree } from '../dist/tree.js'; export async function deleteResourceAndLinkIfExists( oada: OADAClient, - path: string + path: string, ): Promise { try { await oada.head({ path }); @@ -41,14 +41,14 @@ export function keyFromLocation(r: ConnectionResponse) { export async function postJob( oada: OADAClient, path: string, - job: Json + job: Json, ): Promise<{ _id: string; key: string }> { // 1:Create a resource for the job and keep resource id to return const _id = await oada .post({ path: '/resources', data: job as unknown as Json, - contentType: tree.bookmarks!.services!['*']!.jobs!._type, + contentType: tree.bookmarks.services['*'].jobs._type, }) .then((r) => r.headers['content-location']?.replace(/^\//, '') ?? ''); // Get rid of leading slash for link @@ -57,11 +57,12 @@ export async function postJob( .post({ path, data: { _id }, - contentType: tree.bookmarks!.services!['*']!.jobs!.pending!['*']!._type, + contentType: tree.bookmarks.services['*'].jobs.pending['*']._type, }) .then( (r) => - r.headers['content-location']?.replace(/\/resources\/[^/]+\//, '') ?? '' + r.headers['content-location']?.replace(/\/resources\/[^/]+\//, '') ?? + '', ); // Get rid of resourceId to get the new key return { _id, key }; diff --git a/tsconfig.json b/tsconfig.json index 9a97f10..adc9af5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/node16", + "extends": "@tsconfig/node20", "compilerOptions": { "composite": true, "module": "Node16", @@ -20,8 +20,7 @@ "sourceMap": true, "forceConsistentCasingInFileNames": true, "rootDir": "src", - "outDir": "dist", - "lib": ["ES2021"] + "outDir": "dist" }, "include": ["src"] } diff --git a/yarn.lock b/yarn.lock index 8b23c0c..b818e76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -498,6 +498,7 @@ __metadata: "@oada/types": "npm:^3.5.3" "@sindresorhus/is": "npm:^6.1.0" "@tsconfig/node16": "npm:^16.1.1" + "@tsconfig/node20": "npm:^20.1.4" "@types/chai": "npm:^4.3.11" "@types/clone-deep": "npm:^4.0.4" "@types/convict": "npm:^6.1.6" @@ -561,7 +562,7 @@ __metadata: ts-node: "npm:^10.9.2" tslib: "npm:^2.6.2" typescript: "npm:5.3.3" - xlsx: "https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz" + xlsx: "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" peerDependencies: "@oada/client": ^4.5.5 peerDependenciesMeta: @@ -745,6 +746,13 @@ __metadata: languageName: node linkType: hard +"@tsconfig/node20@npm:^20.1.4": + version: 20.1.4 + resolution: "@tsconfig/node20@npm:20.1.4" + checksum: 10/345dba8074647f6c11b8d78afa76d9c16e3436cb56a8e78fe2060014d33a09f3f4fd6ed81dc90e955d3509f926cd7fd61c6ddfd3d5a1d80758d7844f7cc3a99e + languageName: node + linkType: hard + "@types/cacheable-request@npm:^6.0.1": version: 6.0.1 resolution: "@types/cacheable-request@npm:6.0.1" @@ -1851,12 +1859,12 @@ __metadata: languageName: node linkType: hard -"braces@npm:^3.0.2": - version: 3.0.2 - resolution: "braces@npm:3.0.2" +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" dependencies: - fill-range: "npm:^7.0.1" - checksum: 10/966b1fb48d193b9d155f810e5efd1790962f2c4e0829f8440b8ad236ba009222c501f70185ef732fef17a4c490bb33a03b90dab0631feafbdf447da91e8165b1 + fill-range: "npm:^7.1.1" + checksum: 10/fad11a0d4697a27162840b02b1fad249c1683cbc510cd5bf1a471f2f8085c046d41094308c577a50a03a579dd99d5a6b3724c4b5e8b14df2c4443844cfcda2c6 languageName: node linkType: hard @@ -2645,20 +2653,13 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^16.0.3": +"dotenv@npm:^16.0.3, dotenv@npm:^16.3.1, dotenv@npm:^16.4.3": version: 16.4.5 resolution: "dotenv@npm:16.4.5" checksum: 10/55a3134601115194ae0f924e54473459ed0d9fc340ae610b676e248cca45aa7c680d86365318ea964e6da4e2ea80c4514c1adab5adb43d6867fb57ff068f95c8 languageName: node linkType: hard -"dotenv@npm:^16.3.1, dotenv@npm:^16.4.3": - version: 16.4.3 - resolution: "dotenv@npm:16.4.3" - checksum: 10/f07db902c62c239aef7a7b696b21dfab8e95bf5883bcf23c6b9b55f42578d18b842e6257b4f4ecfdbd024a3ee9949fbaaa4c84807de7ba037c74fe3c70785198 - languageName: node - linkType: hard - "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -3582,12 +3583,12 @@ __metadata: languageName: node linkType: hard -"fill-range@npm:^7.0.1": - version: 7.0.1 - resolution: "fill-range@npm:7.0.1" +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" dependencies: to-regex-range: "npm:^5.0.1" - checksum: 10/e260f7592fd196b4421504d3597cc76f4a1ca7a9488260d533b611fc3cefd61e9a9be1417cb82d3b01ad9f9c0ff2dbf258e1026d2445e26b0cf5148ff4250429 + checksum: 10/a7095cb39e5bc32fada2aa7c7249d3f6b01bd1ce461a61b0adabacccabd9198500c6fb1f68a7c851a657e273fce2233ba869638897f3d7ed2e87a2d89b4436ea languageName: node linkType: hard @@ -4069,9 +4070,9 @@ __metadata: linkType: hard "http-cache-semantics@npm:^4.0.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 10/c9c29508b27c1d81ba78fc1df45dc142dfc039a0871e596db0a2257f08c7e9de16be6a61c3a7c90f4cb0e7dfc1c0277ed8a1ea4bc700b07d4e91ff403ca46d9e + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 10/362d5ed66b12ceb9c0a328fb31200b590ab1b02f4a254a697dc796850cc4385603e75f53ec59f768b2dad3bfa1464bd229f7de278d2899a0e3beffc634b6683f languageName: node linkType: hard @@ -5113,12 +5114,12 @@ __metadata: linkType: hard "micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": - version: 4.0.5 - resolution: "micromatch@npm:4.0.5" + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: - braces: "npm:^3.0.2" + braces: "npm:^3.0.3" picomatch: "npm:^2.3.1" - checksum: 10/a749888789fc15cac0e03273844dbd749f9f8e8d64e70c564bcf06a033129554c789bb9e30d7566d7ff6596611a08e58ac12cf2a05f6e3c9c47c50c4c7e12fa2 + checksum: 10/6bf2a01672e7965eb9941d1f02044fad2bd12486b5553dc1116ff24c09a8723157601dc992e74c911d896175918448762df3b3fd0a6b61037dd1a9766ddfbf58 languageName: node linkType: hard @@ -6221,11 +6222,11 @@ __metadata: linkType: hard "semver@npm:2 || 3 || 4 || 5": - version: 5.7.1 - resolution: "semver@npm:5.7.1" + version: 5.7.2 + resolution: "semver@npm:5.7.2" bin: - semver: ./bin/semver - checksum: 10/fbc71cf00736480ca0dd67f2527cda6e0fde5447af00bd2ce06cb522d510216603a63ed0c6c87d8904507c1a4e8113e628a71424ebd9e0fd7d345ee8ed249690 + semver: bin/semver + checksum: 10/fca14418a174d4b4ef1fecb32c5941e3412d52a4d3d85165924ce3a47fbc7073372c26faf7484ceb4bbc2bde25880c6b97e492473dc7e9708fdfb1c6a02d546e languageName: node linkType: hard @@ -6654,8 +6655,8 @@ __metadata: linkType: hard "tar@npm:^6.0.5, tar@npm:^6.1.11": - version: 6.2.0 - resolution: "tar@npm:6.2.0" + version: 6.2.1 + resolution: "tar@npm:6.2.1" dependencies: chownr: "npm:^2.0.0" fs-minipass: "npm:^2.0.0" @@ -6663,7 +6664,7 @@ __metadata: minizlib: "npm:^2.1.1" mkdirp: "npm:^1.0.3" yallist: "npm:^4.0.0" - checksum: 10/2042bbb14830b5cd0d584007db0eb0a7e933e66d1397e72a4293768d2332449bc3e312c266a0887ec20156dea388d8965e53b4fc5097f42d78593549016da089 + checksum: 10/bfbfbb2861888077fc1130b84029cdc2721efb93d1d1fb80f22a7ac3a98ec6f8972f29e564103bbebf5e97be67ebc356d37fa48dbc4960600a1eb7230fbd1ea0 languageName: node linkType: hard @@ -7003,9 +7004,9 @@ __metadata: linkType: hard "undici@npm:^6.6.2": - version: 6.14.0 - resolution: "undici@npm:6.14.0" - checksum: 10/048aeaadd8e3b5fe3f88dcdcf2fa0dfd5d71abf8c028c0207ae7ac5e8bc165b656d9fe6c7e2b9de45edde5f0a763eb9a35c49ac19aad80bf34e0d0e4ec6cac59 + version: 6.19.8 + resolution: "undici@npm:6.19.8" + checksum: 10/19ae4ba38b029a664d99fd330935ef59136cf99edb04ed821042f27b5a9e84777265fb744c8a7abc83f2059afb019446c69a4ebef07bbc0ed6b2de8d67ef4090 languageName: node linkType: hard @@ -7223,8 +7224,8 @@ __metadata: linkType: hard "ws@npm:^8.16.0": - version: 8.16.0 - resolution: "ws@npm:8.16.0" + version: 8.18.0 + resolution: "ws@npm:8.18.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -7233,7 +7234,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/7c511c59e979bd37b63c3aea4a8e4d4163204f00bd5633c053b05ed67835481995f61a523b0ad2b603566f9a89b34cb4965cb9fab9649fbfebd8f740cea57f17 + checksum: 10/70dfe53f23ff4368d46e4c0b1d4ca734db2c4149c6f68bc62cb16fc21f753c47b35fcc6e582f3bdfba0eaeb1c488cddab3c2255755a5c3eecb251431e42b3ff6 languageName: node linkType: hard @@ -7244,12 +7245,12 @@ __metadata: languageName: node linkType: hard -"xlsx@https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz": - version: 0.20.1 - resolution: "xlsx@https://cdn.sheetjs.com/xlsx-0.20.1/xlsx-0.20.1.tgz" +"xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz": + version: 0.20.2 + resolution: "xlsx@https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz" bin: xlsx: ./bin/xlsx.njs - checksum: 10/b4687c75736e97d5b6c46ab2f33c704ea242ad3c5c18f9cb6a14032b42660347e5ba10e1a1893e0916f00a579089f3144f0ef5a33863acaf7bf3f387842b92e1 + checksum: 10/2d8e0644888f90fa9145ea74ed90b844154ce89c4f0e4f92fcce3f224fa71654da99aa48d99d65ba86eb0632a4858ba2dea7eef8b54fd8bd23954a09d1884aa1 languageName: node linkType: hard