From 8b3d75136bbc7707934291bf2214ab0ce7077bbc Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 12 Feb 2024 15:04:49 -0800 Subject: [PATCH 01/11] lint: work around Endo lint bug https://github.com/endojs/endo/pull/2032 --- .eslintrc.cjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 3e04adce836..3c6f9098ddb 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -47,6 +47,9 @@ module.exports = { plugins: ['@typescript-eslint', 'prettier'], extends: ['@agoric', 'plugin:ava/recommended'], rules: { + // UNTIL on Endo with https://github.com/endojs/endo/pull/2032 + '@endo/no-nullish-coalescing': 'off', + '@typescript-eslint/prefer-ts-expect-error': 'warn', '@typescript-eslint/no-floating-promises': 'error', // so that floating-promises can be explicitly permitted with void operator From 3aeabd2afe13a4ee09f7a1d5e8724234cbdd4a7a Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 12 Feb 2024 15:03:20 -0800 Subject: [PATCH 02/11] lint: conform array-foreach --- packages/telemetry/src/index.js | 8 ++++---- packages/telemetry/src/slog-to-otel.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/telemetry/src/index.js b/packages/telemetry/src/index.js index eda0cd01826..4b043c93271 100644 --- a/packages/telemetry/src/index.js +++ b/packages/telemetry/src/index.js @@ -34,9 +34,9 @@ export const tryFlushSlogSender = async ( await Promise.resolve(slogSender?.forceFlush?.()).catch(err => { log?.('Failed to flush slog sender', err); if (err.errors) { - err.errors.forEach(error => { + for (const error of err.errors) { log?.('nested error:', error); - }); + } } if (env.SLOGSENDER_FAIL_ON_ERROR) { throw err; @@ -67,12 +67,12 @@ export const getResourceAttributes = ({ } if (OTEL_RESOURCE_ATTRIBUTES) { // Allow overriding resource attributes. - OTEL_RESOURCE_ATTRIBUTES.split(',').forEach(kv => { + for (const kv of OTEL_RESOURCE_ATTRIBUTES.split(',')) { const match = kv.match(/^([^=]*)=(.*)$/); if (match) { resourceAttributes[match[1]] = match[2]; } - }); + } } return resourceAttributes; }; diff --git a/packages/telemetry/src/slog-to-otel.js b/packages/telemetry/src/slog-to-otel.js index 45591d35035..3fdd63d2ad0 100644 --- a/packages/telemetry/src/slog-to-otel.js +++ b/packages/telemetry/src/slog-to-otel.js @@ -54,9 +54,9 @@ const serializeInto = (value, prefix, target = {}, depth = 3) => { } else { const proto = Object.getPrototypeOf(value); if (proto == null || proto === Object.prototype) { - Object.entries(value).forEach(([key, nested]) => - serializeInto(nested, `${prefix}.${key}`, target, depth), - ); + for (const [key, nested] of Object.entries(value)) { + serializeInto(nested, `${prefix}.${key}`, target, depth); + } return target; } } From cf8ebe13315b0d768aa1de7dbaf1bfd2f1a42936 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 5 Feb 2024 15:04:16 -0800 Subject: [PATCH 03/11] chore(types): MakeSlogSender --- packages/telemetry/src/flight-recorder.js | 13 +++++++++++++ packages/telemetry/src/index.js | 3 +++ packages/telemetry/src/ingest-slog-entrypoint.js | 2 +- packages/telemetry/src/make-slog-sender.js | 4 ++-- packages/telemetry/src/otel-and-flight-recorder.js | 3 +++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 17588e930bc..57fb9e335f5 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -1,3 +1,4 @@ +// @ts-check /// // https://github.com/Agoric/agoric-sdk/issues/3742#issuecomment-1028451575 @@ -70,6 +71,13 @@ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { return arenaSize; }; +/** + * @param {{ + * circularBufferSize?: number, + * stateDir?: string, + * circularBufferFilename?: string + * }} opts + */ export const makeMemoryMappedCircularBuffer = async ({ circularBufferSize = DEFAULT_CBUF_SIZE, stateDir = '/tmp', @@ -227,6 +235,11 @@ export const makeMemoryMappedCircularBuffer = async ({ return { readCircBuf, writeCircBuf, writeJSON }; }; +/** + * Loaded dynamically by makeSlogSender() + * + * @type {import('./index.js').MakeSlogSender} + */ export const makeSlogSender = async opts => { const { writeJSON } = await makeMemoryMappedCircularBuffer(opts); return Object.assign(writeJSON, { diff --git a/packages/telemetry/src/index.js b/packages/telemetry/src/index.js index 4b043c93271..269d989753e 100644 --- a/packages/telemetry/src/index.js +++ b/packages/telemetry/src/index.js @@ -13,6 +13,9 @@ export * from './make-slog-sender.js'; * shutdown?: () => Promise; * }} SlogSender */ +/** + * @typedef {(opts: import('./index.js').MakeSlogSenderOptions) => SlogSender | undefined} MakeSlogSender + */ /** * @typedef {MakeSlogSenderCommonOptions & Record} MakeSlogSenderOptions * @typedef {object} MakeSlogSenderCommonOptions diff --git a/packages/telemetry/src/ingest-slog-entrypoint.js b/packages/telemetry/src/ingest-slog-entrypoint.js index 06d025ab719..bdb5985faad 100755 --- a/packages/telemetry/src/ingest-slog-entrypoint.js +++ b/packages/telemetry/src/ingest-slog-entrypoint.js @@ -74,7 +74,7 @@ async function run() { if (!flush) { return; } - await slogSender.forceFlush(); + await slogSender.forceFlush?.(); fs.writeFileSync(progressFileName, JSON.stringify(progress)); }; diff --git a/packages/telemetry/src/make-slog-sender.js b/packages/telemetry/src/make-slog-sender.js index 6f29e197592..76d00c2d59f 100644 --- a/packages/telemetry/src/make-slog-sender.js +++ b/packages/telemetry/src/make-slog-sender.js @@ -19,7 +19,7 @@ export const DEFAULT_SLOGSENDER_AGENT = 'self'; const filterTruthy = arr => /** @type {any[]} */ (arr.filter(Boolean)); /** - * @param {import('./index.js').MakeSlogSenderOptions} opts + * @type {import('./index.js').MakeSlogSender} */ export const makeSlogSender = async (opts = {}) => { const { env = {}, stateDir: stateDirOption, ...otherOpts } = opts; @@ -88,7 +88,7 @@ export const makeSlogSender = async (opts = {}) => { slogSenderModules.map(async moduleIdentifier => import(moduleIdentifier) .then( - /** @param {{makeSlogSender: (opts: {}) => Promise}} module */ ({ + /** @param {{makeSlogSender: import('./index.js').MakeSlogSender}} module */ ({ makeSlogSender: maker, }) => { if (typeof maker !== 'function') { diff --git a/packages/telemetry/src/otel-and-flight-recorder.js b/packages/telemetry/src/otel-and-flight-recorder.js index d1aa2b24c97..53818c6d04f 100644 --- a/packages/telemetry/src/otel-and-flight-recorder.js +++ b/packages/telemetry/src/otel-and-flight-recorder.js @@ -1,6 +1,9 @@ import { NonNullish } from '@agoric/assert'; import { makeSlogSender as makeSlogSenderFromEnv } from './make-slog-sender.js'; +/** + * @param {import('./index.js').MakeSlogSenderOptions} opts + */ export const makeSlogSender = async opts => { const { SLOGFILE: _1, SLOGSENDER: _2, ...otherEnv } = opts.env || {}; From 45fad0e7f00a1978fdbd03211081abdb081505a7 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 6 Feb 2024 12:23:09 -0800 Subject: [PATCH 04/11] refactor(flight-recorder): makeSlogSenderFromBuffer --- packages/telemetry/src/flight-recorder.js | 24 ++++++++++++------- .../telemetry/test/test-flight-recorder.js | 15 +++++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 57fb9e335f5..fea15a0357f 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -225,14 +225,25 @@ export const makeMemoryMappedCircularBuffer = async ({ ); }; - const writeJSON = (obj, jsonObj = serializeSlogObj(obj)) => { + return { readCircBuf, writeCircBuf }; +}; + +/** + * + * @param {Pick>, 'writeCircBuf'>} circBuf + */ +export const makeSlogSenderFromBuffer = ({ writeCircBuf }) => { + const writeJSON = obj => { + const jsonObj = serializeSlogObj(obj); // Prepend a newline so that the file can be more easily manipulated. const data = new TextEncoder().encode(`\n${jsonObj}`); // console.log('have obj', obj, data); writeCircBuf(data); }; - - return { readCircBuf, writeCircBuf, writeJSON }; + return Object.assign(writeJSON, { + forceFlush: async () => {}, + usesJsonObject: true, + }); }; /** @@ -241,9 +252,6 @@ export const makeMemoryMappedCircularBuffer = async ({ * @type {import('./index.js').MakeSlogSender} */ export const makeSlogSender = async opts => { - const { writeJSON } = await makeMemoryMappedCircularBuffer(opts); - return Object.assign(writeJSON, { - forceFlush: async () => {}, - usesJsonObject: true, - }); + const { writeCircBuf } = await makeMemoryMappedCircularBuffer(opts); + return makeSlogSenderFromBuffer({ writeCircBuf }); }; diff --git a/packages/telemetry/test/test-flight-recorder.js b/packages/telemetry/test/test-flight-recorder.js index f2b3c81eb8a..e3fbb45dbf3 100644 --- a/packages/telemetry/test/test-flight-recorder.js +++ b/packages/telemetry/test/test-flight-recorder.js @@ -1,15 +1,18 @@ import tmp from 'tmp'; import { test } from './prepare-test-env-ava.js'; -import { makeMemoryMappedCircularBuffer } from '../src/flight-recorder.js'; +import { + makeMemoryMappedCircularBuffer, + makeSlogSenderFromBuffer, +} from '../src/flight-recorder.js'; test('flight-recorder sanity', async t => { const { name: tmpFile, removeCallback } = tmp.fileSync(); - const { writeJSON: slogSender, readCircBuf } = - await makeMemoryMappedCircularBuffer({ - circularBufferSize: 512, - circularBufferFilename: tmpFile, - }); + const { readCircBuf, writeCircBuf } = await makeMemoryMappedCircularBuffer({ + circularBufferSize: 512, + circularBufferFilename: tmpFile, + }); + const slogSender = makeSlogSenderFromBuffer({ writeCircBuf }); slogSender({ type: 'start' }); const len0 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT); From be2221b3078acfef3953b15630fd54453fa7613e Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 6 Feb 2024 16:17:06 -0800 Subject: [PATCH 05/11] refactor: fsp --- packages/telemetry/src/flight-recorder.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index fea15a0357f..25df3ac916b 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -14,8 +14,8 @@ // no coherency problem, and the speed is unaffected by disk write speeds. import BufferFromFile from 'bufferfromfile'; -import { promises as fsPromises } from 'fs'; -import path from 'path'; +import fsp from 'node:fs/promises'; +import path from 'node:path'; import { serializeSlogObj } from './serialize-slog-obj.js'; const { Fail } = assert; @@ -37,7 +37,7 @@ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { return undefined; } // If the file doesn't exist, or is not large enough, create it. - const stbuf = await fsPromises.stat(bufferFile).catch(e => { + const stbuf = await fsp.stat(bufferFile).catch(e => { if (e.code === 'ENOENT') { return undefined; } @@ -58,8 +58,8 @@ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { header.setBigUint64(I_CIRC_START, 0n); header.setBigUint64(I_CIRC_END, 0n); - await fsPromises.mkdir(path.dirname(bufferFile), { recursive: true }); - await fsPromises.writeFile(bufferFile, headerBuf); + await fsp.mkdir(path.dirname(bufferFile), { recursive: true }); + await fsp.writeFile(bufferFile, headerBuf); if (stbuf && stbuf.size >= circularBufferSize) { // File is big enough. @@ -67,7 +67,7 @@ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { } // Increase the file size. - await fsPromises.truncate(bufferFile, circularBufferSize); + await fsp.truncate(bufferFile, circularBufferSize); return arenaSize; }; From c0c715b7d7bd991902ff68f1b062d98bce987cc7 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 6 Feb 2024 16:17:49 -0800 Subject: [PATCH 06/11] chore(flight-record): require buffer size --- packages/telemetry/src/flight-recorder.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 25df3ac916b..4ad2e0a3516 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -32,10 +32,14 @@ const I_ARENA_START = 4 * BigUint64Array.BYTES_PER_ELEMENT; const RECORD_HEADER_SIZE = BigUint64Array.BYTES_PER_ELEMENT; +/** + * Initializes a circular buffer with the given size, creating the buffer file if it doesn't exist or is not large enough. + * + * @param {string} bufferFile - the file path for the circular buffer + * @param {number} circularBufferSize - the size of the circular buffer + * @returns {Promise} the size of the initialized circular buffer + */ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { - if (!circularBufferSize) { - return undefined; - } // If the file doesn't exist, or is not large enough, create it. const stbuf = await fsp.stat(bufferFile).catch(e => { if (e.code === 'ENOENT') { From 7622c94a5516e5b7b4372eaf0ddca2ee75226b0e Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 13 Feb 2024 15:12:11 -0800 Subject: [PATCH 07/11] refactor: IO out of CircularBuffer --- packages/telemetry/src/flight-recorder.js | 173 +++++++++++++--------- 1 file changed, 107 insertions(+), 66 deletions(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 4ad2e0a3516..2e885015107 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -75,53 +75,16 @@ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { return arenaSize; }; +/** @typedef {Awaited>} CircularBuffer */ + /** - * @param {{ - * circularBufferSize?: number, - * stateDir?: string, - * circularBufferFilename?: string - * }} opts + * + * @param {bigint} arenaSize + * @param {DataView} header + * @param {(outbuf: Uint8Array, readStart: number, firstReadLength: number) => void} readRecord + * @param {(record: Uint8Array, firstWriteLength: number, circEnd: bigint) => Promise} writeRecord */ -export const makeMemoryMappedCircularBuffer = async ({ - circularBufferSize = DEFAULT_CBUF_SIZE, - stateDir = '/tmp', - circularBufferFilename, -}) => { - const filename = circularBufferFilename || `${stateDir}/${DEFAULT_CBUF_FILE}`; - // console.log({ circularBufferFilename, filename }); - - const newArenaSize = await initializeCircularBuffer( - filename, - circularBufferSize, - ); - - /** - * @type {Uint8Array} - * BufferFromFile mmap()s the file into the process address space. - */ - const fileBuf = BufferFromFile(filename).Uint8Array(); - const header = new DataView(fileBuf.buffer, 0, I_ARENA_START); - - // Detect the arena size from the header, if not initialized. - const hdrArenaSize = header.getBigUint64(I_ARENA_SIZE); - const arenaSize = newArenaSize || hdrArenaSize; - - const hdrMagic = header.getBigUint64(I_MAGIC); - SLOG_MAGIC === hdrMagic || - Fail`${filename} is not a slog buffer; wanted magic ${SLOG_MAGIC}, got ${hdrMagic}`; - arenaSize === hdrArenaSize || - Fail`${filename} arena size mismatch; wanted ${arenaSize}, got ${hdrArenaSize}`; - const arena = new Uint8Array( - fileBuf.buffer, - header.byteLength, - Number(arenaSize), - ); - - /** - * @param {Uint8Array} outbuf - * @param {number} [offset] offset relative to the current trailing edge (circStart) of the data - * @returns {IteratorResult} - */ +function finishCircularBuffer(arenaSize, header, readRecord, writeRecord) { const readCircBuf = (outbuf, offset = 0) => { offset + outbuf.byteLength <= arenaSize || Fail`Reading past end of circular buffer`; @@ -144,19 +107,13 @@ export const makeMemoryMappedCircularBuffer = async ({ // The data is contiguous, like ---AAABBB--- return { done: true, value: undefined }; } - outbuf.set(arena.subarray(readStart, readStart + firstReadLength)); - if (firstReadLength < outbuf.byteLength) { - outbuf.set( - arena.subarray(0, outbuf.byteLength - firstReadLength), - firstReadLength, - ); - } + readRecord(outbuf, readStart, firstReadLength); return { done: false, value: outbuf }; }; - /** @param {Uint8Array} data */ - const writeCircBuf = data => { - if (RECORD_HEADER_SIZE + data.byteLength > arena.byteLength) { + /** @type {(data: Uint8Array) => Promise} */ + const writeCircBuf = async data => { + if (RECORD_HEADER_SIZE + data.byteLength > arenaSize) { // The data is too big to fit in the arena, so skip it. const tooBigRecord = JSON.stringify({ type: 'slog-record-too-big', @@ -165,14 +122,17 @@ export const makeMemoryMappedCircularBuffer = async ({ data = new TextEncoder().encode(tooBigRecord); } - if (RECORD_HEADER_SIZE + data.byteLength > arena.byteLength) { + if (RECORD_HEADER_SIZE + data.byteLength > arenaSize) { // Silently drop, it just doesn't fit. return; } + // Allocate for the data and a header const record = new Uint8Array(RECORD_HEADER_SIZE + data.byteLength); + // Set the data, after the header record.set(data, RECORD_HEADER_SIZE); + // Set the size in the header const lengthPrefix = new DataView(record.buffer); lengthPrefix.setBigUint64(0, BigInt(data.byteLength)); @@ -218,18 +178,96 @@ export const makeMemoryMappedCircularBuffer = async ({ ); } - arena.set(record.subarray(0, firstWriteLength), Number(circEnd)); - if (firstWriteLength < record.byteLength) { - // Write to the beginning of the arena. - arena.set(record.subarray(firstWriteLength, record.byteLength), 0); - } header.setBigUint64( I_CIRC_END, (circEnd + BigInt(record.byteLength)) % arenaSize, ); + + return writeRecord(record, firstWriteLength, circEnd); }; return { readCircBuf, writeCircBuf }; +} + +/** + * @param {{ + * circularBufferSize?: number, + * stateDir?: string, + * circularBufferFilename?: string + * }} opts + */ +export const makeMemoryMappedCircularBuffer = async ({ + circularBufferSize = DEFAULT_CBUF_SIZE, + stateDir = '/tmp', + circularBufferFilename, +}) => { + const filename = circularBufferFilename || `${stateDir}/${DEFAULT_CBUF_FILE}`; + + const newArenaSize = await initializeCircularBuffer( + filename, + circularBufferSize, + ); + + /** + * @type {Uint8Array} + * BufferFromFile mmap()s the file into the process address space. + */ + const fileBuf = BufferFromFile(filename).Uint8Array(); + const header = new DataView(fileBuf.buffer, 0, I_ARENA_START); + + // Detect the arena size from the header, if not initialized. + const hdrArenaSize = header.getBigUint64(I_ARENA_SIZE); + const arenaSize = newArenaSize || hdrArenaSize; + + const hdrMagic = header.getBigUint64(I_MAGIC); + SLOG_MAGIC === hdrMagic || + Fail`${filename} is not a slog buffer; wanted magic ${SLOG_MAGIC}, got ${hdrMagic}`; + arenaSize === hdrArenaSize || + Fail`${filename} arena size mismatch; wanted ${arenaSize}, got ${hdrArenaSize}`; + const arena = new Uint8Array( + fileBuf.buffer, + header.byteLength, + Number(arenaSize), + ); + + /** + * + * @param {Uint8Array} outbuf + * @param {number} readStart + * @param {number} firstReadLength + */ + + function readRecord(outbuf, readStart, firstReadLength) { + outbuf.set(arena.subarray(readStart, readStart + firstReadLength)); + if (firstReadLength < outbuf.byteLength) { + outbuf.set( + arena.subarray(0, outbuf.byteLength - firstReadLength), + firstReadLength, + ); + } + } + + /** + * + * @param {Uint8Array} record + * @param {number} firstWriteLength + * @param {bigint} circEnd + */ + const writeRecord = (record, firstWriteLength, circEnd) => { + arena.set(record.subarray(0, firstWriteLength), Number(circEnd)); + if (firstWriteLength < record.byteLength) { + // Write to the beginning of the arena. + arena.set(record.subarray(firstWriteLength, record.byteLength), 0); + } + return Promise.resolve(); + }; + + /** + * @param {Uint8Array} outbuf + * @param {number} [offset] offset relative to the current trailing edge (circStart) of the data + * @returns {IteratorResult} + */ + return finishCircularBuffer(arenaSize, header, readRecord, writeRecord); }; /** @@ -237,15 +275,18 @@ export const makeMemoryMappedCircularBuffer = async ({ * @param {Pick>, 'writeCircBuf'>} circBuf */ export const makeSlogSenderFromBuffer = ({ writeCircBuf }) => { - const writeJSON = obj => { - const jsonObj = serializeSlogObj(obj); + /** @type {Promise} */ + let toWrite = Promise.resolve(); + const writeJSON = (obj, serialized = serializeSlogObj(obj)) => { // Prepend a newline so that the file can be more easily manipulated. - const data = new TextEncoder().encode(`\n${jsonObj}`); + const data = new TextEncoder().encode(`\n${serialized}`); // console.log('have obj', obj, data); - writeCircBuf(data); + toWrite = toWrite.then(() => writeCircBuf(data)); }; return Object.assign(writeJSON, { - forceFlush: async () => {}, + forceFlush: async () => { + await toWrite; + }, usesJsonObject: true, }); }; From 8d9cb7abe96e8905f5aaa0927e02914ef09279c4 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 9 Feb 2024 17:32:59 -0800 Subject: [PATCH 08/11] feat: simple CircularBuffer with fs offsets --- packages/telemetry/src/flight-recorder.js | 99 ++++++++++++++- .../telemetry/test/test-flight-recorder.js | 120 +++++++++++------- 2 files changed, 170 insertions(+), 49 deletions(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 2e885015107..2cd6953b885 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -1,4 +1,5 @@ // @ts-check +/* global Buffer */ /// // https://github.com/Agoric/agoric-sdk/issues/3742#issuecomment-1028451575 @@ -14,6 +15,7 @@ // no coherency problem, and the speed is unaffected by disk write speeds. import BufferFromFile from 'bufferfromfile'; +import fs from 'node:fs'; import fsp from 'node:fs/promises'; import path from 'node:path'; import { serializeSlogObj } from './serialize-slog-obj.js'; @@ -75,7 +77,7 @@ const initializeCircularBuffer = async (bufferFile, circularBufferSize) => { return arenaSize; }; -/** @typedef {Awaited>} CircularBuffer */ +/** @typedef {Awaited>} CircularBuffer */ /** * @@ -189,6 +191,99 @@ function finishCircularBuffer(arenaSize, header, readRecord, writeRecord) { return { readCircBuf, writeCircBuf }; } +/** + * Variant of makeMemoryMappedCircularBuffer that writes to a file instead of using BufferFromFile + * + * @param {{ + * circularBufferSize?: number, + * stateDir?: string, + * circularBufferFilename?: string + * }} opts + */ +export const makeSimpleCircularBuffer = async ({ + circularBufferSize = DEFAULT_CBUF_SIZE, + stateDir = '/tmp', + circularBufferFilename, +}) => { + const filename = circularBufferFilename || `${stateDir}/${DEFAULT_CBUF_FILE}`; + + const newArenaSize = await initializeCircularBuffer( + filename, + circularBufferSize, + ); + + const file = await fsp.open(filename, 'r+'); + + const headerBuffer = Buffer.alloc(I_ARENA_START); + + await file.read({ + buffer: headerBuffer, + length: I_ARENA_START, + position: 0, + }); + const header = new DataView(headerBuffer.buffer); + + // Detect the arena size from the header, if not initialized. + const hdrArenaSize = header.getBigUint64(I_ARENA_SIZE); + const arenaSize = newArenaSize || hdrArenaSize; + + const hdrMagic = header.getBigUint64(I_MAGIC); + SLOG_MAGIC === hdrMagic || + Fail`${filename} is not a slog buffer; wanted magic ${SLOG_MAGIC}, got ${hdrMagic}`; + arenaSize === hdrArenaSize || + Fail`${filename} arena size mismatch; wanted ${arenaSize}, got ${hdrArenaSize}`; + + /** @type {(outbuf: Uint8Array, readStart: number, firstReadLength: number) => void} */ + const readRecord = (outbuf, readStart, firstReadLength) => { + const bytesRead = fs.readSync(file.fd, outbuf, { + length: firstReadLength, + position: Number(readStart) + I_ARENA_START, + }); + assert.equal(bytesRead, firstReadLength, 'Too few bytes read'); + + if (bytesRead < outbuf.byteLength) { + fs.readSync(file.fd, outbuf, { + offset: firstReadLength, + length: outbuf.byteLength - firstReadLength, + position: I_ARENA_START, + }); + } + }; + + /** + * Writes to the file, offset by the header size. Also updates the file header. + * + * @param {Uint8Array} record + * @param {number} firstWriteLength + * @param {bigint} circEnd + */ + const writeRecord = async (record, firstWriteLength, circEnd) => { + await file.write( + record, + // TS saying options bag not available + 0, + firstWriteLength, + I_ARENA_START + Number(circEnd), + ); + if (firstWriteLength < record.byteLength) { + // Write to the beginning of the arena. + await file.write( + record, + firstWriteLength, + record.byteLength - firstWriteLength, + I_ARENA_START, + ); + } + + // Write out the updated file header. + // This is somewhat independent of writing the record itself, but it needs + // updating each time a record is written. + await file.write(headerBuffer, undefined, undefined, 0); + }; + + return finishCircularBuffer(arenaSize, header, readRecord, writeRecord); +}; + /** * @param {{ * circularBufferSize?: number, @@ -272,7 +367,7 @@ export const makeMemoryMappedCircularBuffer = async ({ /** * - * @param {Pick>, 'writeCircBuf'>} circBuf + * @param {Pick>, 'writeCircBuf'>} circBuf */ export const makeSlogSenderFromBuffer = ({ writeCircBuf }) => { /** @type {Promise} */ diff --git a/packages/telemetry/test/test-flight-recorder.js b/packages/telemetry/test/test-flight-recorder.js index e3fbb45dbf3..d71ec2196b2 100644 --- a/packages/telemetry/test/test-flight-recorder.js +++ b/packages/telemetry/test/test-flight-recorder.js @@ -1,56 +1,82 @@ +import fs from 'node:fs'; import tmp from 'tmp'; import { test } from './prepare-test-env-ava.js'; import { makeMemoryMappedCircularBuffer, + makeSimpleCircularBuffer, makeSlogSenderFromBuffer, } from '../src/flight-recorder.js'; -test('flight-recorder sanity', async t => { - const { name: tmpFile, removeCallback } = tmp.fileSync(); - const { readCircBuf, writeCircBuf } = await makeMemoryMappedCircularBuffer({ - circularBufferSize: 512, - circularBufferFilename: tmpFile, - }); - const slogSender = makeSlogSenderFromBuffer({ writeCircBuf }); - slogSender({ type: 'start' }); - - const len0 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT); - const { done: done0 } = readCircBuf(len0); - t.false(done0, 'readCircBuf should not be done'); - const dv0 = new DataView(len0.buffer); - const buf0 = new Uint8Array(Number(dv0.getBigUint64(0))); - const { done: done0b } = readCircBuf(buf0, len0.byteLength); - t.false(done0b, 'readCircBuf should not be done'); - const buf0Str = new TextDecoder().decode(buf0); - t.is(buf0Str, `\n{"type":"start"}`, `start compare failed`); - - const last = 500; - for (let i = 0; i < last; i += 1) { - slogSender({ type: 'iteration', iteration: i }); - } - - let offset = 0; - const len1 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT); - for (let i = 490; i < last; i += 1) { - const { done: done1 } = readCircBuf(len1, offset); - offset += len1.byteLength; - t.false(done1, `readCircBuf ${i} should not be done`); - const dv1 = new DataView(len1.buffer); - const buf1 = new Uint8Array(Number(dv1.getBigUint64(0))); - const { done: done1b } = readCircBuf(buf1, offset); - offset += buf1.byteLength; - t.false(done1b, `readCircBuf ${i} should not be done`); - const buf1Str = new TextDecoder().decode(buf1); - t.is( - buf1Str, - `\n{"type":"iteration","iteration":${i}}`, - `iteration ${i} compare failed`, - ); - } - - const { done: done2 } = readCircBuf(len1, offset); - t.assert(done2, `readCircBuf ${last} should be done`); - // console.log({ tmpFile }); - removeCallback(); +const bufferTests = test.macro( + /** + * + * @param {*} t + * @param {{makeBuffer: Function}} input + */ + async (t, input) => { + const BUFFER_SIZE = 512; + + const { name: tmpFile, removeCallback } = tmp.fileSync(); + const { readCircBuf, writeCircBuf } = await input.makeBuffer({ + circularBufferSize: BUFFER_SIZE, + circularBufferFilename: tmpFile, + }); + const slogSender = makeSlogSenderFromBuffer({ writeCircBuf }); + slogSender({ type: 'start' }); + await slogSender.forceFlush(); + t.is(fs.readFileSync(tmpFile, { encoding: 'utf8' }).length, BUFFER_SIZE); + + const len0 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT); + const { done: done0 } = readCircBuf(len0); + t.false(done0, 'readCircBuf should not be done'); + const dv0 = new DataView(len0.buffer); + const buf0 = new Uint8Array(Number(dv0.getBigUint64(0))); + const { done: done0b } = readCircBuf(buf0, len0.byteLength); + t.false(done0b, 'readCircBuf should not be done'); + const buf0Str = new TextDecoder().decode(buf0); + t.is(buf0Str, `\n{"type":"start"}`, `start compare failed`); + + const last = 500; + for (let i = 0; i < last; i += 1) { + slogSender({ type: 'iteration', iteration: i }); + await slogSender.forceFlush(); + t.is( + fs.readFileSync(tmpFile, { encoding: 'utf8' }).length, + BUFFER_SIZE, + `iteration ${i} length mismatch`, + ); + } + + let offset = 0; + const len1 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT); + for (let i = 490; i < last; i += 1) { + const { done: done1 } = readCircBuf(len1, offset); + offset += len1.byteLength; + t.false(done1, `readCircBuf ${i} should not be done`); + const dv1 = new DataView(len1.buffer); + const buf1 = new Uint8Array(Number(dv1.getBigUint64(0))); + const { done: done1b } = readCircBuf(buf1, offset); + offset += buf1.byteLength; + t.false(done1b, `readCircBuf ${i} should not be done`); + const buf1Str = new TextDecoder().decode(buf1); + t.is( + buf1Str, + `\n{"type":"iteration","iteration":${i}}`, + `iteration ${i} compare failed`, + ); + } + + const { done: done2 } = readCircBuf(len1, offset); + t.assert(done2, `readCircBuf ${last} should be done`); + // console.log({ tmpFile }); + removeCallback(); + }, +); + +test('memory mapped', bufferTests, { + makeBuffer: makeMemoryMappedCircularBuffer, +}); +test('simple', bufferTests, { + makeBuffer: makeSimpleCircularBuffer, }); From a043651eaffc7202bc040899dae7f539972ad9d4 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 13 Feb 2024 06:47:43 -0800 Subject: [PATCH 09/11] test: pre-serialization --- packages/telemetry/test/test-flight-recorder.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/telemetry/test/test-flight-recorder.js b/packages/telemetry/test/test-flight-recorder.js index d71ec2196b2..69a6705bccd 100644 --- a/packages/telemetry/test/test-flight-recorder.js +++ b/packages/telemetry/test/test-flight-recorder.js @@ -69,6 +69,10 @@ const bufferTests = test.macro( const { done: done2 } = readCircBuf(len1, offset); t.assert(done2, `readCircBuf ${last} should be done`); + + slogSender(null, 'PRE-SERIALIZED'); + await slogSender.forceFlush(); + t.truthy(fs.readFileSync(tmpFile).includes('PRE-SERIALIZED')); // console.log({ tmpFile }); removeCallback(); }, From 47a2adda72a5377eda181a425130cdc5a7fd7ff5 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Fri, 9 Feb 2024 17:37:40 -0800 Subject: [PATCH 10/11] feat: use writeSync slogSender --- packages/telemetry/src/flight-recorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 2cd6953b885..050ccdea257 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -392,6 +392,6 @@ export const makeSlogSenderFromBuffer = ({ writeCircBuf }) => { * @type {import('./index.js').MakeSlogSender} */ export const makeSlogSender = async opts => { - const { writeCircBuf } = await makeMemoryMappedCircularBuffer(opts); + const { writeCircBuf } = await makeSimpleCircularBuffer(opts); return makeSlogSenderFromBuffer({ writeCircBuf }); }; From 515ef12c54490d168c7003306107633f755d16fe Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 12 Feb 2024 14:02:15 -0800 Subject: [PATCH 11/11] chore: remove mmap-based CircularBuffer --- packages/telemetry/package.json | 3 +- packages/telemetry/src/flight-recorder.js | 96 ------------------- packages/telemetry/src/frcat-entrypoint.js | 4 +- .../telemetry/test/test-flight-recorder.js | 5 +- patches/bufferfromfile+0.4.377.patch | 9 -- yarn.lock | 78 ++------------- 6 files changed, 10 insertions(+), 185 deletions(-) delete mode 100644 patches/bufferfromfile+0.4.377.patch diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 77653297e16..352ea489cbf 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -37,7 +37,6 @@ "@opentelemetry/semantic-conventions": "~1.9.0", "anylogger": "^0.21.0", "better-sqlite3": "^9.1.1", - "bufferfromfile": "agoric-labs/BufferFromFile#Agoric-built", "tmp": "^0.2.1" }, "devDependencies": { @@ -64,6 +63,6 @@ "workerThreads": false }, "typeCoverage": { - "atLeast": 87.55 + "atLeast": 87.14 } } diff --git a/packages/telemetry/src/flight-recorder.js b/packages/telemetry/src/flight-recorder.js index 050ccdea257..e6c3048fd5b 100644 --- a/packages/telemetry/src/flight-recorder.js +++ b/packages/telemetry/src/flight-recorder.js @@ -2,19 +2,6 @@ /* global Buffer */ /// -// https://github.com/Agoric/agoric-sdk/issues/3742#issuecomment-1028451575 -// I'd mmap() a 100MB file, reserve a few bytes for offsets, then use the rest -// as a circular buffer to hold length-prefixed records. The agd process would -// keep writing new events into the RAM window and updating the start/end -// pointers, with some sequencing to make sure the record gets written before -// the pointer is updated. Then, no mattter how abruptly the process is -// terminated, as long as the host computer itself is still running, the on-disk -// file would contain the most recent state, and anybody who reads the file will -// get the most recent state. The host kernel (linux) is under no obligation to -// flush it to disk any particular time, but knows when reads happen, so there's -// no coherency problem, and the speed is unaffected by disk write speeds. - -import BufferFromFile from 'bufferfromfile'; import fs from 'node:fs'; import fsp from 'node:fs/promises'; import path from 'node:path'; @@ -192,8 +179,6 @@ function finishCircularBuffer(arenaSize, header, readRecord, writeRecord) { } /** - * Variant of makeMemoryMappedCircularBuffer that writes to a file instead of using BufferFromFile - * * @param {{ * circularBufferSize?: number, * stateDir?: string, @@ -284,87 +269,6 @@ export const makeSimpleCircularBuffer = async ({ return finishCircularBuffer(arenaSize, header, readRecord, writeRecord); }; -/** - * @param {{ - * circularBufferSize?: number, - * stateDir?: string, - * circularBufferFilename?: string - * }} opts - */ -export const makeMemoryMappedCircularBuffer = async ({ - circularBufferSize = DEFAULT_CBUF_SIZE, - stateDir = '/tmp', - circularBufferFilename, -}) => { - const filename = circularBufferFilename || `${stateDir}/${DEFAULT_CBUF_FILE}`; - - const newArenaSize = await initializeCircularBuffer( - filename, - circularBufferSize, - ); - - /** - * @type {Uint8Array} - * BufferFromFile mmap()s the file into the process address space. - */ - const fileBuf = BufferFromFile(filename).Uint8Array(); - const header = new DataView(fileBuf.buffer, 0, I_ARENA_START); - - // Detect the arena size from the header, if not initialized. - const hdrArenaSize = header.getBigUint64(I_ARENA_SIZE); - const arenaSize = newArenaSize || hdrArenaSize; - - const hdrMagic = header.getBigUint64(I_MAGIC); - SLOG_MAGIC === hdrMagic || - Fail`${filename} is not a slog buffer; wanted magic ${SLOG_MAGIC}, got ${hdrMagic}`; - arenaSize === hdrArenaSize || - Fail`${filename} arena size mismatch; wanted ${arenaSize}, got ${hdrArenaSize}`; - const arena = new Uint8Array( - fileBuf.buffer, - header.byteLength, - Number(arenaSize), - ); - - /** - * - * @param {Uint8Array} outbuf - * @param {number} readStart - * @param {number} firstReadLength - */ - - function readRecord(outbuf, readStart, firstReadLength) { - outbuf.set(arena.subarray(readStart, readStart + firstReadLength)); - if (firstReadLength < outbuf.byteLength) { - outbuf.set( - arena.subarray(0, outbuf.byteLength - firstReadLength), - firstReadLength, - ); - } - } - - /** - * - * @param {Uint8Array} record - * @param {number} firstWriteLength - * @param {bigint} circEnd - */ - const writeRecord = (record, firstWriteLength, circEnd) => { - arena.set(record.subarray(0, firstWriteLength), Number(circEnd)); - if (firstWriteLength < record.byteLength) { - // Write to the beginning of the arena. - arena.set(record.subarray(firstWriteLength, record.byteLength), 0); - } - return Promise.resolve(); - }; - - /** - * @param {Uint8Array} outbuf - * @param {number} [offset] offset relative to the current trailing edge (circStart) of the data - * @returns {IteratorResult} - */ - return finishCircularBuffer(arenaSize, header, readRecord, writeRecord); -}; - /** * * @param {Pick>, 'writeCircBuf'>} circBuf diff --git a/packages/telemetry/src/frcat-entrypoint.js b/packages/telemetry/src/frcat-entrypoint.js index 63dfc264bca..34b8f042012 100755 --- a/packages/telemetry/src/frcat-entrypoint.js +++ b/packages/telemetry/src/frcat-entrypoint.js @@ -5,7 +5,7 @@ import '@endo/init'; -import { makeMemoryMappedCircularBuffer } from './flight-recorder.js'; +import { makeSimpleCircularBuffer } from './flight-recorder.js'; const main = async () => { const files = process.argv.slice(2); @@ -14,7 +14,7 @@ const main = async () => { } for await (const file of files) { - const { readCircBuf } = await makeMemoryMappedCircularBuffer({ + const { readCircBuf } = await makeSimpleCircularBuffer({ circularBufferFilename: file, circularBufferSize: 0, }); diff --git a/packages/telemetry/test/test-flight-recorder.js b/packages/telemetry/test/test-flight-recorder.js index 69a6705bccd..27cfada2c3a 100644 --- a/packages/telemetry/test/test-flight-recorder.js +++ b/packages/telemetry/test/test-flight-recorder.js @@ -3,11 +3,11 @@ import tmp from 'tmp'; import { test } from './prepare-test-env-ava.js'; import { - makeMemoryMappedCircularBuffer, makeSimpleCircularBuffer, makeSlogSenderFromBuffer, } from '../src/flight-recorder.js'; +// Factored this way to support multiple implementations, which at one point there were const bufferTests = test.macro( /** * @@ -78,9 +78,6 @@ const bufferTests = test.macro( }, ); -test('memory mapped', bufferTests, { - makeBuffer: makeMemoryMappedCircularBuffer, -}); test('simple', bufferTests, { makeBuffer: makeSimpleCircularBuffer, }); diff --git a/patches/bufferfromfile+0.4.377.patch b/patches/bufferfromfile+0.4.377.patch deleted file mode 100644 index 51690d3cc72..00000000000 --- a/patches/bufferfromfile+0.4.377.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/node_modules/bufferfromfile/proto/wtools/amid/bufferFromFile/Main.js b/node_modules/bufferfromfile/proto/wtools/amid/bufferFromFile/Main.js -index 4c4c41b..9b88d11 100644 ---- a/node_modules/bufferfromfile/proto/wtools/amid/bufferFromFile/Main.js -+++ b/node_modules/bufferfromfile/proto/wtools/amid/bufferFromFile/Main.js -@@ -1,3 +1,4 @@ -+// @ts-nocheck - ( function _BufferFromFile_js_() - { // - diff --git a/yarn.lock b/yarn.lock index 8679affb8bf..a40ad1e043b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1624,21 +1624,6 @@ npmlog "^6.0.2" write-file-atomic "^4.0.1" -"@mapbox/node-pre-gyp@1.0.9": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc" - integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@noble/hashes@^1", "@noble/hashes@^1.0.0": version "1.1.2" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" @@ -2883,14 +2868,6 @@ are-docs-informative@^0.0.2: resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - are-we-there-yet@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" @@ -3299,14 +3276,6 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bufferfromfile@agoric-labs/BufferFromFile#Agoric-built: - version "0.4.377" - resolved "https://codeload.github.com/agoric-labs/BufferFromFile/tar.gz/691031035856fadba2603e52f4411160d7f34ed6" - dependencies: - "@mapbox/node-pre-gyp" "1.0.9" - node-gyp "^9.3.1" - wbasenodejscpp latest - builtin-modules@^3.1.0, builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -3647,7 +3616,7 @@ color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.2, color-support@^1.1.3: +color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== @@ -3768,7 +3737,7 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== -console-control-strings@^1.0.0, console-control-strings@^1.1.0: +console-control-strings@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= @@ -5253,21 +5222,6 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21" integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - gauge@^4.0.3: version "4.0.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" @@ -6681,7 +6635,7 @@ make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.1.0: +make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -7422,7 +7376,7 @@ node-gyp-build@^4.3.0, node-gyp-build@^4.4.0, node-gyp-build@^4.5.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== -node-gyp@^9.0.0, node-gyp@^9.3.1: +node-gyp@^9.0.0: version "9.4.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== @@ -7610,16 +7564,6 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" @@ -7681,11 +7625,6 @@ nx@15.9.4, "nx@>=14.8.1 < 16": "@nrwl/nx-win32-arm64-msvc" "15.9.4" "@nrwl/nx-win32-x64-msvc" "15.9.4" -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - object-hash@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" @@ -8937,7 +8876,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -9877,11 +9816,6 @@ walk-up-path@^1.0.0: resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e" integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg== -wbasenodejscpp@latest: - version "0.3.134" - resolved "https://registry.yarnpkg.com/wbasenodejscpp/-/wbasenodejscpp-0.3.134.tgz#c23c06bd8c3e170afe59084cfa6e7d41c07b0ffa" - integrity sha512-B3cyJ14XNKe7gfu+pQPdCkDKTMu4DiFS/iKWE+1AhroChj+hLiNYCcoUuvsjEocQtBPItjb57mbhdKCJ80pjCA== - wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -9944,7 +9878,7 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@^1.1.2, wide-align@^1.1.5: +wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==