Compare commits

...

14 Commits

Author SHA1 Message Date
Julien Fontanet
f72b9ca4ca update to undici@6 2023-12-22 14:24:06 +01:00
Julien Fontanet
0d69e2396f various updates 2023-12-22 14:13:58 +01:00
b-Nollet
c20b43e230 adapting getResource usage 2023-12-22 14:13:58 +01:00
b-Nollet
5aa6298dd9 adapting getResource usage 2023-12-22 14:13:58 +01:00
b-Nollet
6ab93c78fe re-handling redirections 2023-12-22 14:13:58 +01:00
b-Nollet
8eda65ab88 returning full response 2023-12-22 14:13:58 +01:00
b-Nollet
589228eb9e reverting unrelated change 2023-12-22 14:13:56 +01:00
b-Nollet
be86c0dd32 forgotten params 2023-12-22 14:10:18 +01:00
b-Nollet
11f67b1039 formatting 2023-12-22 14:10:18 +01:00
b-Nollet
cd767256d1 undici getResource xen-api 2023-12-22 14:10:18 +01:00
b-Nollet
64448426cd moving client to index 2023-12-22 14:10:18 +01:00
b-Nollet
54d1785e08 xml-rpc undici 2023-12-22 14:10:17 +01:00
b-Nollet
1ed98d3607 chore(xen-api): json-rpc undici 2023-12-22 14:10:17 +01:00
Julien Fontanet
0d4cf48410 feat(xo-cli rest): explicit error if not registered
Fixes https://xcp-ng.org/forum/post/68698
2023-12-22 11:33:08 +01:00
16 changed files with 147 additions and 91 deletions

View File

@@ -31,7 +31,7 @@ export class PoolMetadataBackup {
const poolDir = `${DIR_XO_POOL_METADATA_BACKUPS}/${schedule.id}/${pool.$id}` const poolDir = `${DIR_XO_POOL_METADATA_BACKUPS}/${schedule.id}/${pool.$id}`
const dir = `${poolDir}/${formatFilenameDate(timestamp)}` const dir = `${poolDir}/${formatFilenameDate(timestamp)}`
const stream = await this._exportPoolMetadata() const stream = (await this._exportPoolMetadata()).body
const fileName = `${dir}/data` const fileName = `${dir}/data`
const metadata = JSON.stringify( const metadata = JSON.stringify(

View File

@@ -108,10 +108,12 @@ class Vdi {
} else { } else {
// raw export without nbd or vhd exports needs a resource stream // raw export without nbd or vhd exports needs a resource stream
const vdiName = await this.getField('VDI', ref, 'name_label') const vdiName = await this.getField('VDI', ref, 'name_label')
stream = await this.getResource(cancelToken, '/export_raw_vdi/', { stream = (
query, await this.getResource(cancelToken, '/export_raw_vdi/', {
task: await this.task_create(`Exporting content of VDI ${vdiName}`), query,
}) task: await this.task_create(`Exporting content of VDI ${vdiName)}`),
})
).body
if (nbdClient !== undefined && format === VDI_FORMAT_VHD) { if (nbdClient !== undefined && format === VDI_FORMAT_VHD) {
const taskRef = await this.task_create(`Exporting content of VDI ${vdiName} using NBD`) const taskRef = await this.task_create(`Exporting content of VDI ${vdiName} using NBD`)
stream = await createNbdVhdStream(nbdClient, stream) stream = await createNbdVhdStream(nbdClient, stream)

View File

@@ -502,13 +502,15 @@ class Vm {
exportedVmRef = vmRef exportedVmRef = vmRef
} }
try { try {
const stream = await this.getResource(cancelToken, '/export/', { const stream = (
query: { await this.getResource(cancelToken, '/export/', {
ref: exportedVmRef, query: {
use_compression: compress === 'zstd' ? 'zstd' : compress === true || compress === 'gzip' ? 'true' : 'false', ref: exportedVmRef,
}, use_compression: compress === 'zstd' ? 'zstd' : compress === true || compress === 'gzip' ? 'true' : 'false',
task: taskRef, },
}) task: taskRef,
})
).body
if (useSnapshot) { if (useSnapshot) {
stream.once('end', destroySnapshot).once('error', destroySnapshot) stream.once('end', destroySnapshot).once('error', destroySnapshot)

View File

@@ -35,6 +35,7 @@
<!--packages-start--> <!--packages-start-->
- vhd-lib patch - vhd-lib patch
- xo-cli minor
- xo-server minor - xo-server minor
- xo-web minor - xo-web minor

View File

@@ -44,14 +44,14 @@ defer(async ($defer, rawArgs) => {
const vdi = await resolveRecord(xapi, 'VDI', args[1]) const vdi = await resolveRecord(xapi, 'VDI', args[1])
// https://xapi-project.github.io/xen-api/snapshots.html#downloading-a-disk-or-snapshot // https://xapi-project.github.io/xen-api/snapshots.html#downloading-a-disk-or-snapshot
const exportStream = await xapi.getResource(token, '/export_raw_vdi/', { const response = await xapi.getResource(token, '/export_raw_vdi/', {
query: { query: {
format: raw ? 'raw' : 'vhd', format: raw ? 'raw' : 'vhd',
vdi: vdi.$ref, vdi: vdi.$ref,
}, },
}) })
console.warn('Export task:', exportStream.headers['task-id']) console.warn('Export task:', response.headers['task-id'])
const top = createTop() const top = createTop()
const progressStream = createProgress() const progressStream = createProgress()
@@ -63,5 +63,5 @@ defer(async ($defer, rawArgs) => {
}, 1e3) }, 1e3)
) )
await pipeline(exportStream, progressStream, throttle(bps), createOutputStream(args[2])) await pipeline(response.body, progressStream, throttle(bps), createOutputStream(args[2]))
})(process.argv.slice(2)).catch(console.error.bind(console, 'error')) })(process.argv.slice(2)).catch(console.error.bind(console, 'error'))

View File

@@ -37,17 +37,17 @@ defer(async ($defer, rawArgs) => {
process.on('SIGINT', cancel) process.on('SIGINT', cancel)
// https://xapi-project.github.io/xen-api/importexport.html // https://xapi-project.github.io/xen-api/importexport.html
const exportStream = await xapi.getResource(token, '/export/', { const response = await xapi.getResource(token, '/export/', {
query: { query: {
ref: (await resolveRecord(xapi, 'VM', args[1])).$ref, ref: (await resolveRecord(xapi, 'VM', args[1])).$ref,
use_compression: zstd ? 'zstd' : gzip ? 'true' : 'false', use_compression: zstd ? 'zstd' : gzip ? 'true' : 'false',
}, },
}) })
console.warn('Export task:', exportStream.headers['task-id']) console.warn('Export task:', response.headers['task-id'])
await pipeline( await pipeline(
exportStream, response.body,
createProgress({ time: 1e3 }, p => console.warn(formatProgress(p))), createProgress({ time: 1e3 }, p => console.warn(formatProgress(p))),
createOutputStream(args[2]) createOutputStream(args[2])
) )

View File

@@ -5,6 +5,7 @@ import ms from 'ms'
import httpRequest from 'http-request-plus' import httpRequest from 'http-request-plus'
import map from 'lodash/map.js' import map from 'lodash/map.js'
import noop from 'lodash/noop.js' import noop from 'lodash/noop.js'
import { Client } from 'undici'
import { coalesceCalls } from '@vates/coalesce-calls' import { coalesceCalls } from '@vates/coalesce-calls'
import { Collection } from 'xo-collection' import { Collection } from 'xo-collection'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
@@ -394,37 +395,54 @@ export class Xapi extends EventEmitter {
} }
let url = new URL('http://localhost') let url = new URL('http://localhost')
url.protocol = this._url.protocol
url.pathname = pathname
url.search = new URLSearchParams(query)
await this._setHostAddressInUrl(url, host) await this._setHostAddressInUrl(url, host)
const response = await this._addSyncStackTrace( const response = await this._addSyncStackTrace(
pRetry( pRetry(
async () => async () => {
httpRequest(url, { const client = new Client(url, {
rejectUnauthorized: !this._allowUnauthorized, connect: {
rejectUnauthorized: !this._allowUnauthorized,
// Support XS <= 6.5 with Node => 12
minVersion: 'TLSv1',
},
})
// this is an inactivity timeout (unclear in Node doc) return client
timeout: this._httpInactivityTimeout, .request({
method: 'GET',
path: pathname,
query,
maxRedirections: 0,
headersTimeout: this._httpInactivityTimeout,
bodyTimeout: this._httpInactivityTimeout,
agent: this.httpAgent,
maxRedirects: 0, signal: $cancelToken,
})
// Support XS <= 6.5 with Node => 12 .then(response => {
minVersion: 'TLSv1', const { statusCode } = response
agent: this.httpAgent, if (((statusCode / 100) | 0) === 2) {
return response
signal: $cancelToken, }
}), const error = new Error(`${response.statusCode} ${response.statusMessage}`)
Object.defineProperty(error, 'response', { value: response })
throw error
})
},
{ {
when: error => error.response !== undefined && error.response.statusCode === 302, when: error => error.response !== undefined && error.response.statusCode === 302,
onRetry: async error => { onRetry: async error => {
const response = error.response const response = error.response
if (response === undefined) { if (response === undefined || response.body === undefined) {
throw error throw error
} }
response.destroy() response.body.on('error', noop)
response.body.destroy()
url = await this._replaceHostAddressInUrl(new URL(response.headers.location, url)) url = await this._replaceHostAddressInUrl(new URL(response.headers.location, url))
query = Object.fromEntries(url.searchParams.entries())
pathname = url.pathname
url.pathname = url.search = ''
}, },
} }
) )
@@ -945,14 +963,18 @@ export class Xapi extends EventEmitter {
const { hostname } = url const { hostname } = url
url.hostnameRaw = hostname[0] === '[' ? hostname.slice(1, -1) : hostname url.hostnameRaw = hostname[0] === '[' ? hostname.slice(1, -1) : hostname
this._humanId = `${this._auth.user ?? 'unknown'}@${url.hostname}` const client = new Client(url, {
this._transport = this._createTransport({ connect: {
secureOptions: {
minVersion: 'TLSv1', minVersion: 'TLSv1',
rejectUnauthorized: !this._allowUnauthorized, rejectUnauthorized: !this._allowUnauthorized,
}, },
url, })
this._humanId = `${this._auth.user ?? 'unknown'}@${url.hostname}`
this._transport = this._createTransport({
agent: this.httpAgent, agent: this.httpAgent,
client,
url,
}) })
this._url = url this._url = url
} }

View File

@@ -28,7 +28,7 @@
"xen-api": "./cli.mjs" "xen-api": "./cli.mjs"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=18"
}, },
"dependencies": { "dependencies": {
"@vates/coalesce-calls": "^0.1.0", "@vates/coalesce-calls": "^0.1.0",
@@ -48,7 +48,8 @@
"promise-toolbox": "^0.21.0", "promise-toolbox": "^0.21.0",
"proxy-agent": "^6.3.1", "proxy-agent": "^6.3.1",
"pw": "0.0.4", "pw": "0.0.4",
"xmlrpc": "^1.3.2", "undici": "^6.2.1",
"xmlrpc-parser": "^1.0.3",
"xo-collection": "^0.5.0" "xo-collection": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,4 +1,3 @@
import httpRequestPlus from 'http-request-plus'
import { format, parse } from 'json-rpc-protocol' import { format, parse } from 'json-rpc-protocol'
import XapiError from '../_XapiError.mjs' import XapiError from '../_XapiError.mjs'
@@ -6,18 +5,19 @@ import XapiError from '../_XapiError.mjs'
import UnsupportedTransport from './_UnsupportedTransport.mjs' import UnsupportedTransport from './_UnsupportedTransport.mjs'
// https://github.com/xenserver/xenadmin/blob/0df39a9d83cd82713f32d24704852a0fd57b8a64/XenModel/XenAPI/Session.cs#L403-L433 // https://github.com/xenserver/xenadmin/blob/0df39a9d83cd82713f32d24704852a0fd57b8a64/XenModel/XenAPI/Session.cs#L403-L433
export default ({ secureOptions, url, agent }) => { export default ({ agent, client, url }) => {
url = new URL('./jsonrpc', Object.assign(new URL('http://localhost'), url)) url = new URL('./jsonrpc', Object.assign(new URL('http://localhost'), url))
const path = url.pathname + url.search
return async function (method, args) { return async function (method, args) {
const res = await httpRequestPlus(url, { const res = await client.request({
...secureOptions,
body: format.request(0, method, args), body: format.request(0, method, args),
headers: { headers: {
Accept: 'application/json', Accept: 'application/json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
method: 'POST', method: 'POST',
path,
agent, agent,
}) })
@@ -26,7 +26,7 @@ export default ({ secureOptions, url, agent }) => {
throw new UnsupportedTransport() throw new UnsupportedTransport()
} }
const response = parse(await res.text()) const response = parse(await res.body.text())
if (response.type === 'response') { if (response.type === 'response') {
return response.result return response.result

View File

@@ -1,18 +1,8 @@
import xmlrpc from 'xmlrpc' import { XmlRpcMessage, XmlRpcResponse } from 'xmlrpc-parser'
import { promisify } from 'promise-toolbox'
import XapiError from '../_XapiError.mjs'
import prepareXmlRpcParams from './_prepareXmlRpcParams.mjs' import prepareXmlRpcParams from './_prepareXmlRpcParams.mjs'
import XapiError from '../_XapiError.mjs'
const logError = error => { import UnsupportedTransport from './_UnsupportedTransport.mjs'
if (error.res) {
console.error('XML-RPC Error: %s (response status %s)', error.message, error.res.statusCode)
console.error('%s', error.body)
}
throw error
}
const parseResult = result => { const parseResult = result => {
const status = result.Status const status = result.Status
@@ -30,16 +20,31 @@ const parseResult = result => {
return result.Value return result.Value
} }
export default ({ secureOptions, url: { hostnameRaw, pathname, port, protocol }, agent }) => { export default ({ agent, client, url }) => {
const secure = protocol === 'https:' url = new URL('./xmlrpc', Object.assign(new URL('http://localhost'), url))
const client = (secure ? xmlrpc.createSecureClient : xmlrpc.createClient)({ const path = url.pathname + url.search
...(secure ? secureOptions : undefined),
agent,
host: hostnameRaw,
pathname,
port,
})
const call = promisify(client.methodCall, client)
return (method, args) => call(method, prepareXmlRpcParams(args)).then(parseResult, logError) return async function (method, args) {
const message = new XmlRpcMessage(method, prepareXmlRpcParams(args))
const res = await client.request({
body: message.xml(),
headers: {
Accept: 'text/xml',
'Content-Type': 'text/xml',
},
method: 'POST',
path,
agent,
})
if (res.headers['content-type'] !== 'text/xml' && res.headers['content-type'] !== 'application/xml') {
throw new UnsupportedTransport()
}
const xml = await res.body.text()
const response = await new XmlRpcResponse().parse(xml)
return parseResult(response.params[0])
}
} }

View File

@@ -134,6 +134,12 @@ export async function rest(args) {
const { allowUnauthorized, server, token } = await config.load() const { allowUnauthorized, server, token } = await config.load()
if (server === undefined) {
const errorMessage =
'Please use `xo-cli --register` to associate with an XO instance first.\n\nSee `xo-cli --help` for more info.'
throw errorMessage
}
const baseUrl = server const baseUrl = server
const baseOpts = { const baseOpts = {
headers: { headers: {

View File

@@ -693,7 +693,7 @@ ${entriesWithMissingStats.map(({ listItem }) => listItem).join('\n')}`
payload.vm_uuid = xapiObject.uuid payload.vm_uuid = xapiObject.uuid
} }
// JSON is not well formed, can't use the default node parser // JSON is not well formed, can't use the default node parser
return JSON5.parse(await (await xapi.getResource('/rrd_updates', payload)).text()) return JSON5.parse(await (await xapi.getResource('/rrd_updates', payload)).body.text())
} }
} }

View File

@@ -245,7 +245,7 @@ export default class XapiStats {
start: timestamp, start: timestamp,
}, },
}) })
.then(response => response.text().then(JSON5.parse)) .then(response => response.body.text().then(JSON5.parse))
.catch(err => { .catch(err => {
delete this.#hostCache[hostUuid][step] delete this.#hostCache[hostUuid][step]
throw err throw err

View File

@@ -7,7 +7,7 @@ export default {
exportPoolMetadata($cancelToken) { exportPoolMetadata($cancelToken) {
return this.getResource($cancelToken, PATH_DB_DUMP, { return this.getResource($cancelToken, PATH_DB_DUMP, {
task: this.task_create('Export pool metadata'), task: this.task_create('Export pool metadata'),
}) }).then(response => response.body)
}, },
// Restore the XAPI database from an XML backup // Restore the XAPI database from an XML backup

View File

@@ -187,14 +187,14 @@ export default class RestApi {
const host = req.xapiObject const host = req.xapiObject
res.setHeader('content-type', 'text/plain') res.setHeader('content-type', 'text/plain')
await pipeline(await host.$xapi.getResource('/audit_log', { host }), compressMaybe(req, res)) await pipeline((await host.$xapi.getResource('/audit_log', { host })).body, compressMaybe(req, res))
}, },
async 'logs.tar'(req, res) { async 'logs.tar'(req, res) {
const host = req.xapiObject const host = req.xapiObject
res.setHeader('content-type', 'application/x-tar') res.setHeader('content-type', 'application/x-tar')
await pipeline(await host.$xapi.getResource('/host_logs_download', { host }), compressMaybe(req, res)) await pipeline((await host.$xapi.getResource('/host_logs_download', { host })).body, compressMaybe(req, res))
}, },
async missing_patches(req, res) { async missing_patches(req, res) {

View File

@@ -2107,6 +2107,11 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.55.0.tgz#b721d52060f369aa259cf97392403cb9ce892ec6"
integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA== integrity sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==
"@fastify/busboy@^2.0.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.0.tgz#0709e9f4cb252351c609c6e6d8d6779a8d25edff"
integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==
"@fontsource/poppins@^5.0.8": "@fontsource/poppins@^5.0.8":
version "5.0.8" version "5.0.8"
resolved "https://registry.yarnpkg.com/@fontsource/poppins/-/poppins-5.0.8.tgz#a1c5540aedb3719a36eba5c7c5dfaa3aed3c9f80" resolved "https://registry.yarnpkg.com/@fontsource/poppins/-/poppins-5.0.8.tgz#a1c5540aedb3719a36eba5c7c5dfaa3aed3c9f80"
@@ -18842,16 +18847,21 @@ sass@^1.38.1:
immutable "^4.0.0" immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0" source-map-js ">=0.6.2 <2.0.0"
sax@1.2.x, sax@~1.2.4: sax-parser@^2.0.2:
version "1.2.4" version "2.0.2"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" resolved "https://registry.yarnpkg.com/sax-parser/-/sax-parser-2.0.2.tgz#7b3b4a25fc69bf4e729ad5f0f98430205d461689"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== integrity sha512-EjLxlFjZdmv/cpOwV+klYEeOYjR2Dc9C495d2Ruk+N6xknrOnIfjSum2a63hfi9Vox2fCsjYc3NuDVo0YkGpjg==
sax@>=0.6, sax@>=0.6.0: sax@>=0.6, sax@>=0.6.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0"
integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==
sax@~1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
scheduler@^0.20.2: scheduler@^0.20.2:
version "0.20.2" version "0.20.2"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
@@ -20889,6 +20899,13 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici@^6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/undici/-/undici-6.2.1.tgz#554293044619e065d986c37a4c92185c3bc02121"
integrity sha512-7Wa9thEM6/LMnnKtxJHlc8SrTlDmxqJecgz1iy8KlsN0/iskQXOQCuPkrZLXbElPaSw5slFFyKIKXyJ3UtbApw==
dependencies:
"@fastify/busboy" "^2.0.0"
unicode-canonical-property-names-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
@@ -21113,6 +21130,11 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
utf8-base64@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/utf8-base64/-/utf8-base64-0.1.2.tgz#555806c458f9ba3f089c3ebe0c5f6198348bb57b"
integrity sha512-DNeEx/I7HruiVsfk/DbEl4bpdRR/mv5p6FGDFZVyA8wqdMOqYp0CeCgW4/DzsPIW/skOq5Bxv49/eYfvAYJTWg==
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -22064,11 +22086,6 @@ xml2js@^0.4.19, xml2js@^0.4.23:
sax ">=0.6.0" sax ">=0.6.0"
xmlbuilder "~11.0.0" xmlbuilder "~11.0.0"
xmlbuilder@8.2.x:
version "8.2.2"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773"
integrity sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==
xmlbuilder@^15.1.1: xmlbuilder@^15.1.1:
version "15.1.1" version "15.1.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
@@ -22079,13 +22096,13 @@ xmlbuilder@~11.0.0:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
xmlrpc@^1.3.2: xmlrpc-parser@^1.0.3:
version "1.3.2" version "1.0.3"
resolved "https://registry.yarnpkg.com/xmlrpc/-/xmlrpc-1.3.2.tgz#26b2ea347848d028aac7e7514b5351976de3e83d" resolved "https://registry.yarnpkg.com/xmlrpc-parser/-/xmlrpc-parser-1.0.3.tgz#94f21bb74daaa2290a51471635c73f8b6dc1f3d9"
integrity sha512-jQf5gbrP6wvzN71fgkcPPkF4bF/Wyovd7Xdff8d6/ihxYmgETQYSuTc+Hl+tsh/jmgPLro/Aro48LMFlIyEKKQ== integrity sha512-0197DF6MrKFoiaccl2GuB5mcc3F0jSebPLHIqsahpau4yyztg34bVZDhc6HGzs5hji801prnytCKTo4Kdpa7Rw==
dependencies: dependencies:
sax "1.2.x" sax-parser "^2.0.2"
xmlbuilder "8.2.x" utf8-base64 "^0.1.2"
xok@^1.0.0: xok@^1.0.0:
version "1.0.0" version "1.0.0"