diff --git a/packages/upload-client/src/index.js b/packages/upload-client/src/index.js index 71d5c3da4..3d528258d 100644 --- a/packages/upload-client/src/index.js +++ b/packages/upload-client/src/index.js @@ -131,3 +131,18 @@ async function uploadBlockStream(conf, blocks, options = {}) { await Upload.add(conf, root, shards, options) return root } + +/** + * @param {import('./types').InvocationConfig} conf + * @param {(writer: ReturnType) => Promise} task + * @param {import('./types').UploadOptions} [options] + */ +export const uploadWith = async (conf, task, options = {}) => { + const channel = UnixFS.createUploadChannel() + const writer = UnixFS.createDirectoryWriter(channel) + const result = uploadBlockStream(conf, channel.readable, options) + await task(writer) + await writer.close() + await channel.writer.close() + return result +} diff --git a/packages/upload-client/src/unixfs.js b/packages/upload-client/src/unixfs.js index 942e2ea95..2fd48a256 100644 --- a/packages/upload-client/src/unixfs.js +++ b/packages/upload-client/src/unixfs.js @@ -174,3 +174,129 @@ async function collect(collectable) { ) return chunks } + +/** + * @typedef {{ + * readable: ReadableStream + * writable: WritableStream + * writer: import('@ipld/unixfs').View + * }} UploadChannel + */ + +/** + * Create a new upload channel that can be used to write UnixFS files and + * directories. + * + * @param {QueuingStrategy} [strategy] + * @returns {UploadChannel} + */ +export const createUploadChannel = (strategy = queuingStrategy) => { + const { readable, writable } = new TransformStream({}, strategy) + const writer = UnixFS.createWriter({ writable, settings }) + return { readable, writable, writer } +} + +/** + * @param {object} options + * @param {import('@ipld/unixfs').View} options.writer + */ +export const createDirectoryWriter = (options) => new DirectoryWriter(options) + +class FileWriter { + /** + * @param {object} options + * @param {import('@ipld/unixfs').View} options.writer + */ + constructor({ writer }) { + this.writer = UnixFS.createFileWriter(writer) + } + /** + * @param {Uint8Array} chunk + */ + write(chunk) { + return this.writer.write(chunk) + } + close() { + if (this.result) { + return this.result + } else { + return (this.result = this.writer.close()) + } + } +} + +class DirectoryWriter { + /** + * @param {object} options + * @param {import('@ipld/unixfs').View} options.writer + */ + constructor({ writer }) { + this.writer = writer + /** @type {Map} */ + this.entries = new Map() + } + + /** + * @param {string} path + */ + createDirectory(path) { + /** @type {DirectoryWriter} */ + let directory = this + const at = [] + for (const name of path.split('/')) { + if (name !== '' && name !== '.') { + at.push(name) + let writer = directory.entries.get(name) + if (writer == null) { + writer = new DirectoryWriter(this) + directory.entries.set(name, writer) + } + + if (!(writer instanceof DirectoryWriter)) { + throw new Error( + `Can not create directory at ${at.join( + '/' + )}, because there is a file with the same name` + ) + } + + directory = writer + } + } + return directory + } + + /** + * @param {string} path + */ + createFile(path) { + const parts = path.split('/') + const name = /** @type {string} */ (parts.pop()) + let directory = this.createDirectory(parts.join('/')) + + if (directory.entries.has(name)) { + throw new Error( + `Can not create a file at "${path}" because there is already a file or directory with the same name"` + ) + } + + const writer = new FileWriter(this) + directory.entries.set(name, writer) + return writer + } + + async close() { + const writer = + this.entries.size <= SHARD_THRESHOLD + ? UnixFS.createDirectoryWriter(this.writer) + : UnixFS.createShardedDirectoryWriter(this.writer) + + const promises = [...this.entries].map(async ([name, entry]) => { + const link = await entry.close() + writer.set(name, link) + }) + + await Promise.all(promises) + return await writer.close() + } +} diff --git a/packages/upload-client/test/index.test.js b/packages/upload-client/test/index.test.js index 3c298f5e9..b8d807a65 100644 --- a/packages/upload-client/test/index.test.js +++ b/packages/upload-client/test/index.test.js @@ -7,7 +7,12 @@ import * as CBOR from '@ucanto/transport/cbor' import * as Signer from '@ucanto/principal/ed25519' import * as StoreCapabilities from '@web3-storage/capabilities/store' import * as UploadCapabilities from '@web3-storage/capabilities/upload' -import { uploadFile, uploadDirectory, uploadCAR } from '../src/index.js' +import { + uploadFile, + uploadDirectory, + uploadCAR, + uploadWith, +} from '../src/index.js' import { serviceSigner } from './fixtures.js' import { randomBlock, randomBytes } from './helpers/random.js' import { toCAR } from './helpers/car.js' @@ -458,3 +463,116 @@ describe('uploadCAR', () => { assert.equal(carCIDs.length, 2) }) }) + +describe('incremental uploader', () => { + it('incremental upload', async () => { + const space = await Signer.generate() + const agent = await Signer.generate() + const files = [ + new File([await randomBytes(128)], '1.txt'), + new File([await randomBytes(32)], '2.txt'), + ] + + /** @type {import('../src/types').CARLink?} */ + let carCID = null + + const proofs = await Promise.all([ + StoreCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), + UploadCapabilities.add.delegate({ + issuer: space, + audience: agent, + with: space.did(), + expiration: Infinity, + }), + ]) + + /** @type {Omit} */ + const res = { + status: 'upload', + headers: { 'x-test': 'true' }, + url: 'http://localhost:9200', + with: space.did(), + } + + const service = mockService({ + store: { + add: provide(StoreCapabilities.add, ({ capability, invocation }) => { + assert.equal(invocation.issuer.did(), agent.did()) + assert.equal(invocation.capabilities.length, 1) + const invCap = invocation.capabilities[0] + assert.equal(invCap.can, StoreCapabilities.add.can) + assert.equal(invCap.with, space.did()) + return { + ...res, + link: /** @type {import('../src/types').CARLink} */ ( + capability.nb.link + ), + } + }), + }, + upload: { + add: provide(UploadCapabilities.add, ({ invocation }) => { + assert.equal(invocation.issuer.did(), agent.did()) + assert.equal(invocation.capabilities.length, 1) + const invCap = invocation.capabilities[0] + assert.equal(invCap.can, UploadCapabilities.add.can) + assert.equal(invCap.with, space.did()) + assert.equal(invCap.nb?.shards?.length, 1) + assert.equal(String(invCap.nb?.shards?.[0]), carCID?.toString()) + if (!invCap.nb) throw new Error('nb must be present') + return invCap.nb + }), + }, + }) + + const server = Server.create({ + id: serviceSigner, + service, + decoder: CAR, + encoder: CBOR, + }) + const connection = Client.connect({ + id: serviceSigner, + encoder: CAR, + decoder: CBOR, + channel: server, + }) + + const dataCID = await uploadWith( + { + issuer: agent, + with: space.did(), + proofs, + audience: serviceSigner, + }, + async (writer) => { + console.log('writing') + const onetxt = writer.createFile(files[0].name) + onetxt.write(new Uint8Array(await files[0].arrayBuffer())) + await onetxt.close() + + const twotxt = writer.createFile(files[1].name) + await twotxt.write(new Uint8Array(await files[1].arrayBuffer())) + }, + { + connection, + onShardStored: (meta) => { + carCID = meta.cid + }, + } + ) + + assert(service.store.add.called) + assert.equal(service.store.add.callCount, 1) + assert(service.upload.add.called) + assert.equal(service.upload.add.callCount, 1) + + assert(carCID) + assert(dataCID) + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fa857bdb..29104d9cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2049,7 +2049,7 @@ packages: engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} dependencies: comment-parser: 1.3.1 - esquery: 1.4.0 + esquery: 1.5.0 jsdoc-type-pratt-parser: 3.1.0 dev: true @@ -2674,11 +2674,11 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils/4.1.2_eslint@8.33.0: - resolution: {integrity: sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==} + /@eslint-community/eslint-utils/4.4.0_eslint@8.33.0: + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 dependencies: eslint: 8.33.0 eslint-visitor-keys: 3.3.0 @@ -2705,7 +2705,7 @@ packages: dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.4.1 + espree: 9.5.0 globals: 13.20.0 ignore: 5.2.4 import-fresh: 3.3.0 @@ -3155,7 +3155,7 @@ packages: peerDependencies: typescript: ^3 || ^4 dependencies: - esquery: 1.4.0 + esquery: 1.5.0 typescript: 4.9.5 dev: true @@ -3473,7 +3473,7 @@ packages: /@types/better-sqlite3/7.6.3: resolution: {integrity: sha512-YS64N9SNDT/NAvou3QNdzAu3E2om/W/0dhORimtPGLef+zSK5l1vDzfsWb4xgXOgfhtOI5ZDTRxnvRPb22AIVQ==} dependencies: - '@types/node': 18.15.3 + '@types/node': 18.15.5 dev: true /@types/body-parser/1.19.2: @@ -3616,12 +3616,8 @@ packages: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true - /@types/node/18.15.3: - resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} - /@types/node/18.15.5: resolution: {integrity: sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==} - dev: true /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -3718,7 +3714,7 @@ packages: /@types/through/0.0.30: resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==} dependencies: - '@types/node': 18.15.3 + '@types/node': 18.15.5 dev: true /@types/unist/2.0.6: @@ -3741,12 +3737,6 @@ packages: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: true - /@types/yargs/17.0.22: - resolution: {integrity: sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==} - dependencies: - '@types/yargs-parser': 21.0.0 - dev: true - /@types/yargs/17.0.23: resolution: {integrity: sha512-yuogunc04OnzGQCrfHx+Kk883Q4X0aSwmYZhKjI21m+SVYzjIbrWl8dOOwSv5hf2Um2pdCOXWo9isteZTNXUZQ==} dependencies: @@ -3855,6 +3845,26 @@ packages: - supports-color dev: true + /@typescript-eslint/parser/5.57.0_4vsywjlpuriuw3tl5oq6zy5a64: + resolution: {integrity: sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.57.0 + '@typescript-eslint/types': 5.57.0 + '@typescript-eslint/typescript-estree': 5.57.0_typescript@4.9.5 + debug: 4.3.4 + eslint: 8.33.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser/5.57.0_vgl77cfdswitgr47lm5swmv43m: resolution: {integrity: sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5128,11 +5138,6 @@ packages: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} dev: true - /ci-info/3.7.1: - resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} - engines: {node: '>=8'} - dev: true - /ci-info/3.8.0: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} @@ -6462,7 +6467,7 @@ packages: typescript: '*' dependencies: '@typescript-eslint/eslint-plugin': 5.50.0_go4drrxstycfikanvu45pi4vgq - '@typescript-eslint/parser': 5.50.0_4vsywjlpuriuw3tl5oq6zy5a64 + '@typescript-eslint/parser': 5.57.0_4vsywjlpuriuw3tl5oq6zy5a64 eslint: 8.33.0 eslint-config-standard: 17.0.0_xh3wrndcszbt2l7hdksdjqnjcq eslint-plugin-import: 2.27.5_ufewo3pl5nnmz6lltvjrdi2hii @@ -6473,6 +6478,28 @@ packages: - supports-color dev: true + /eslint-config-standard-with-typescript/30.0.0_jo4jc4suruallj56mai7w4wzte: + resolution: {integrity: sha512-/Ltst1BCZCWrGmqprLHBkTwuAbcoQrR8uMeSzZAv1vHKIVg+2nFje+DULA30SW01yCNhnx0a8yhZBkR0ZZPp+w==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 5.57.0_5t5646cukn2kik5kiydglap3vi + '@typescript-eslint/parser': 5.57.0_vgl77cfdswitgr47lm5swmv43m + eslint: 8.36.0 + eslint-config-standard: 17.0.0_htxjg2emk4phzexndh6sfdkv2u + eslint-plugin-import: 2.27.5_74llxljztmzze2ez7aakaiqyti + eslint-plugin-n: 15.6.1_eslint@8.36.0 + eslint-plugin-promise: 6.1.1_eslint@8.36.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + /eslint-config-standard-with-typescript/34.0.1_jo4jc4suruallj56mai7w4wzte: resolution: {integrity: sha512-J7WvZeLtd0Vr9F+v4dZbqJCLD16cbIy4U+alJMq4MiXdpipdBM3U5NkXaGUjePc4sb1ZE01U9g6VuTBpHHz1fg==} peerDependencies: @@ -6816,7 +6843,25 @@ packages: debug: 4.3.4 escape-string-regexp: 4.0.0 eslint: 8.33.0 - esquery: 1.4.0 + esquery: 1.5.0 + semver: 7.3.8 + spdx-expression-parse: 3.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-jsdoc/39.7.5_eslint@8.36.0: + resolution: {integrity: sha512-6L90P0AnZcE4ra7nocolp9vTjgVr2wEZ7jPnEA/X30XAoQPk+wvnaq61n164Tf7Fg4QPpJtRSCPpApOsfWDdNA==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@es-joy/jsdoccomment': 0.36.1 + comment-parser: 1.3.1 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint: 8.36.0 + esquery: 1.5.0 semver: 7.3.8 spdx-expression-parse: 3.0.1 transitivePeerDependencies: @@ -6971,11 +7016,36 @@ packages: eslint: '>=8.28.0' dependencies: '@babel/helper-validator-identifier': 7.19.1 - '@eslint-community/eslint-utils': 4.1.2_eslint@8.33.0 - ci-info: 3.7.1 + '@eslint-community/eslint-utils': 4.4.0_eslint@8.33.0 + ci-info: 3.8.0 clean-regexp: 1.0.0 eslint: 8.33.0 - esquery: 1.4.0 + esquery: 1.5.0 + indent-string: 4.0.0 + is-builtin-module: 3.2.1 + jsesc: 3.0.2 + lodash: 4.17.21 + pluralize: 8.0.0 + read-pkg-up: 7.0.1 + regexp-tree: 0.1.24 + regjsparser: 0.9.1 + safe-regex: 2.1.1 + semver: 7.3.8 + strip-indent: 3.0.0 + dev: true + + /eslint-plugin-unicorn/45.0.2_eslint@8.36.0: + resolution: {integrity: sha512-Y0WUDXRyGDMcKLiwgL3zSMpHrXI00xmdyixEGIg90gHnj0PcHY4moNv3Ppje/kDivdAy5vUeUr7z211ImPv2gw==} + engines: {node: '>=14.18'} + peerDependencies: + eslint: '>=8.28.0' + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + '@eslint-community/eslint-utils': 4.4.0_eslint@8.36.0 + ci-info: 3.8.0 + clean-regexp: 1.0.0 + eslint: 8.36.0 + esquery: 1.5.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 jsesc: 3.0.2 @@ -7090,8 +7160,8 @@ packages: eslint-scope: 7.1.1 eslint-utils: 3.0.0_eslint@8.33.0 eslint-visitor-keys: 3.3.0 - espree: 9.4.1 - esquery: 1.4.0 + espree: 9.5.0 + esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -7169,15 +7239,6 @@ packages: - supports-color dev: true - /espree/9.4.1: - resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.8.2 - acorn-jsx: 5.3.2_acorn@8.8.2 - eslint-visitor-keys: 3.3.0 - dev: true - /espree/9.5.0: resolution: {integrity: sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7192,13 +7253,6 @@ packages: engines: {node: '>=4'} hasBin: true - /esquery/1.4.0: - resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - /esquery/1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} @@ -8077,23 +8131,23 @@ packages: resolution: {integrity: sha512-nDWeib3SxaHZRz0YhRkOnBDT5LAyMx6BXITO5xsocUJh4bSaqn7ha/h9Zlhw0WLtfxSVEXv96kjp/LQts12B9A==} engines: {node: '>=14'} dependencies: - '@typescript-eslint/eslint-plugin': 5.50.0_go4drrxstycfikanvu45pi4vgq - '@typescript-eslint/parser': 5.50.0_4vsywjlpuriuw3tl5oq6zy5a64 - eslint: 8.33.0 - eslint-config-prettier: 8.6.0_eslint@8.33.0 - eslint-config-standard: 17.0.0_xh3wrndcszbt2l7hdksdjqnjcq - eslint-config-standard-with-typescript: 30.0.0_frfgwa7fqjzszldru3sxumpviq - eslint-plugin-etc: 2.0.2_4vsywjlpuriuw3tl5oq6zy5a64 - eslint-plugin-import: 2.27.5_ufewo3pl5nnmz6lltvjrdi2hii - eslint-plugin-jsdoc: 39.7.5_eslint@8.33.0 - eslint-plugin-n: 15.6.1_eslint@8.33.0 + '@typescript-eslint/eslint-plugin': 5.57.0_5t5646cukn2kik5kiydglap3vi + '@typescript-eslint/parser': 5.57.0_vgl77cfdswitgr47lm5swmv43m + eslint: 8.36.0 + eslint-config-prettier: 8.8.0_eslint@8.36.0 + eslint-config-standard: 17.0.0_htxjg2emk4phzexndh6sfdkv2u + eslint-config-standard-with-typescript: 30.0.0_jo4jc4suruallj56mai7w4wzte + eslint-plugin-etc: 2.0.2_vgl77cfdswitgr47lm5swmv43m + eslint-plugin-import: 2.27.5_74llxljztmzze2ez7aakaiqyti + eslint-plugin-jsdoc: 39.7.5_eslint@8.36.0 + eslint-plugin-n: 15.6.1_eslint@8.36.0 eslint-plugin-no-only-tests: 3.1.0 - eslint-plugin-promise: 6.1.1_eslint@8.33.0 - eslint-plugin-react: 7.32.2_eslint@8.33.0 - eslint-plugin-react-hooks: 4.6.0_eslint@8.33.0 - eslint-plugin-unicorn: 45.0.2_eslint@8.33.0 - lint-staged: 13.1.0 - prettier: 2.8.3 + eslint-plugin-promise: 6.1.1_eslint@8.36.0 + eslint-plugin-react: 7.32.2_eslint@8.36.0 + eslint-plugin-react-hooks: 4.6.0_eslint@8.36.0 + eslint-plugin-unicorn: 45.0.2_eslint@8.36.0 + lint-staged: 13.2.0 + prettier: 2.8.7 simple-git-hooks: 2.8.1 typescript: 4.9.5 transitivePeerDependencies: @@ -10972,7 +11026,7 @@ packages: detect-libc: 2.0.1 expand-template: 2.0.3 github-from-package: 0.0.0 - minimist: 1.2.7 + minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 node-abi: 3.31.0 @@ -11078,7 +11132,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 18.15.3 + '@types/node': 18.15.5 long: 5.2.1 /proxy-addr/2.0.7: @@ -12691,7 +12745,7 @@ packages: tsutils: ^3.0.0 typescript: ^4.0.0 dependencies: - '@types/yargs': 17.0.22 + '@types/yargs': 17.0.23 tsutils: 3.21.0_typescript@4.9.5 typescript: 4.9.5 yargs: 17.6.2